Как мне выполнить сброс после увеличения UIScrollView? - PullRequest
15 голосов
/ 15 января 2009

У меня есть график, нарисованный внутри UIScrollView. Это один большой UIView, использующий пользовательский подкласс CATiledLayer в качестве слоя.

Когда я увеличиваю и уменьшаю UIScrollView, я хочу, чтобы график динамически изменял размеры, как это происходит, когда я возвращаю график из viewForZoomingInScrollView. Однако график перерисовывается при новом уровне масштабирования, и я хочу сбросить масштаб преобразования до 1x1, чтобы при следующем увеличении пользователя преобразование начиналось с текущего представления. Если я переустановлю преобразование в Identity в scrollViewDidEndZooming, оно будет работать в симуляторе, но выдает EXC_BAD_ACCSES на устройстве.

Это даже не решает проблему полностью на симуляторе, потому что в следующий раз, когда пользователь увеличивает масштаб, преобразование сбрасывает себя к тому уровню масштабирования, на котором он был, и, таким образом, похоже, если бы я был увеличен до 2x, например, это внезапно в 4x. Когда я заканчиваю масштабирование, оно заканчивается в правильном масштабе, но сам факт масштабирования выглядит плохо.

Итак, во-первых: как мне разрешить графу перерисовывать себя в стандартном масштабе 1x1 после масштабирования, и как у меня плавное масштабирование во всем?

Редактировать: Новые находки Ошибка, кажется, "[CALayer retainCount]: message sent to deallocated instance"

Я никогда не освобождаю никакие слои самостоятельно. Раньше я даже не удалял никакие виды или что-то еще. Эта ошибка возникала при увеличении, а также при повороте. Если я удаляю объект до поворота и добавляю его потом, исключение не выдается. Это не опция для увеличения.

Ответы [ 7 ]

41 голосов
/ 16 января 2009

Я не могу помочь вам с падением, кроме как попросить вас проверить и убедиться, что вы случайно не выпускаете автоматически представление или слой где-то в вашем коде. Я видел, как симулятор обрабатывает время авто-выпусков иначе, чем на устройстве (чаще всего, когда задействованы потоки).

Масштабирование представления - проблема с UIScrollView, с которой я столкнулся. Во время события масштабирования пинч UIScrollView возьмет вид, указанный вами в методе делегата viewForZoomingInScrollView:, и применит к нему преобразование. Это преобразование обеспечивает плавное масштабирование вида без необходимости перерисовывать его каждый кадр. В конце операции масштабирования будет вызван ваш метод делегата scrollViewDidEndZooming:withView:atScale:, который даст вам возможность более качественно отобразить ваше представление с новым масштабным коэффициентом. Как правило, рекомендуется изменить преобразование в представлении на CGAffineTransformIdentity, а затем вручную перерисовать его в новой шкале размеров.

Однако это вызывает проблему, поскольку UIScrollView, по-видимому, не контролирует преобразование представления содержимого, поэтому при следующей операции масштабирования он устанавливает преобразование представления содержимого в соответствии с каким бы ни был общий масштабный коэффициент. Так как вы вручную перерисовали свое представление в соответствии с последним масштабным коэффициентом, оно составляет масштабирование, которое вы видите.

В качестве обходного пути я использую подкласс UIView для своего представления содержимого со следующими определенными методами:

- (void)setTransformWithoutScaling:(CGAffineTransform)newTransform;
{
    [super setTransform:newTransform];
}

- (void)setTransform:(CGAffineTransform)newValue;
{
    [super setTransform:CGAffineTransformScale(newValue, 1.0f / previousScale, 1.0f / previousScale)];
}

где previousScale - переменная экземпляра с плавающей точкой представления. Затем я реализую метод делегирования масштабирования следующим образом:

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale;
{
    [contentView setTransformWithoutScaling:CGAffineTransformIdentity];
// Code to manually redraw view at new scale here
    contentView.previousScale = scale;
    scrollView.contentSize = contentView.frame.size;
}

При этом преобразования, отправляемые в представление содержимого, корректируются в зависимости от масштаба, в котором представление было в последний раз перерисовано. Когда масштабирование по пинчу завершено, преобразование сбрасывается до масштаба 1,0, минуя настройку в обычном методе setTransform:. Похоже, это обеспечивает правильное поведение при масштабировании, позволяя рисовать четкое изображение при завершении масштабирования.

ОБНОВЛЕНИЕ (23.07.2010): iPhone OS 3.2 и выше изменили поведение прокрутки в отношении масштабирования. Теперь UIScrollView будет учитывать преобразование идентичности, которое вы применяете к представлению контента, и предоставит относительный масштабный коэффициент только в -scrollViewDidEndZooming:withView:atScale:. Поэтому приведенный выше код для подкласса UIView необходим только для устройств, работающих под управлением версий iPhone OS старше 3.2.

13 голосов
/ 02 августа 2011

Спасибо за все предыдущие ответы, и вот мое решение.
Реализуйте UIScrollViewDelegate методы:

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return tmv;
}

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
    CGPoint contentOffset = [tmvScrollView contentOffset];   
    CGSize  contentSize   = [tmvScrollView contentSize];
     CGSize  containerSize = [tmv frame].size;

    tmvScrollView.maximumZoomScale = tmvScrollView.maximumZoomScale / scale;
    tmvScrollView.minimumZoomScale = tmvScrollView.minimumZoomScale / scale;

    previousScale *= scale;

    [tmvScrollView setZoomScale:1.0f];

    [tmvScrollView setContentOffset:contentOffset];
    [tmvScrollView setContentSize:contentSize];
    [tmv setFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)];  

    [tmv reloadData];
}
  • tmv - мой подкласс UIView
  • tmvScrollView - выход в UIScrollView
  • set MaximumZoomScale и минимумZoomScale до
  • создать previousScale переменную экземпляра и установить ее значение 1.0f

У меня отлично работает.

BR, Евгений.

7 голосов
/ 03 июня 2015

Я просто искал для сброса при загрузке масштаб по умолчанию, потому что когда я его масштабирую, у scrollview будет та же шкала масштабирования в следующий раз, пока она не будет освобождена.

-(void) viewWillAppear:(BOOL)animated {
      [self.scrollView setZoomScale:1.0f];
}

Изменена игра.

5 голосов
/ 08 мая 2009

У меня есть подробное обсуждение того, как (и почему) работает масштабирование UIScrollView на github.com / andreyvit / ScrollingMadness / .

(Ссылка также содержит описание того, как программно масштабировать UIScrollView, как эмулировать пейджинг в стиле фотоархива + масштабирование + прокрутка, пример проекта и класс ZoomScrollView, который включает в себя некоторые из методов увеличения.)

Цитата:

UIScrollView не имеет понятия «текущий уровень масштабирования», потому что у каждого подпредставления, которое он содержит, может быть свой собственный текущий уровень масштабирования. Обратите внимание, что в UIScrollView нет поля для сохранения текущего уровня масштабирования. Однако мы знаем, что кто-то сохраняет этот уровень масштабирования, потому что, если вы сузите масштабирование подпредставления, затем сбросите его преобразование до CGAffineTransformIdentity, а затем снова нажмите, вы заметите, что предыдущий уровень масштабирования подпредставления был восстановлен.

Действительно, если вы посмотрите на дизассемблирование, это UIView сохраняет свой собственный уровень масштабирования (внутри объекта UIGestureInfo, на который указывает поле _gestureInfo). Он также имеет набор хороших недокументированных методов, таких как zoomScale и setZoomScale:animated:. (Имейте в виду, у него также есть куча методов, связанных с вращением, возможно, скоро мы получим поддержку жестов вращения.)

Однако, если мы создадим новый UIView только для масштабирования и добавим наш реальный масштабируемый вид в качестве его дочернего элемента, мы всегда начнем с уровня масштабирования 1,0. Моя реализация программного масштабирования основана на этом приеме.

3 голосов
/ 12 ноября 2010

Достаточно надежный подход, соответствующий iOS 3.2 и 4.0 и более поздним версиям, заключается в следующем. Вы должны быть готовы предоставить масштабируемое представление (главное подпредставление представления прокрутки) в любом запрошенном масштабе. Затем в scrollViewDidEndZooming: вы удалите размытую масштабно-преобразованную версию этого вида и замените ее новым видом, нарисованным в новом масштабе.

Вам понадобится:

  • Ивар для поддержания предыдущей шкалы; давайте назовем это oldScale

  • Способ идентификации масштабируемого представления как для viewForZoomingInScrollView:, так и для scrollViewDidEndZooming:; здесь я собираюсь применить тег (999)

  • Метод, который создает масштабируемое представление, маркирует его и вставляет в представление прокрутки (здесь я назову его addNewScalableViewAtScale:). Помните, что он должен делать все в соответствии с масштабом - он масштабирует вид и его подпредставления или чертеж, а также подбирает размер содержимого прокрутки для представления.

  • Константы для minimalZoomScale и MaximumZoomScale. Они необходимы, потому что, когда мы заменим масштабированное представление более подробной расширенной версией, система будет думать, что текущий масштаб равен 1, поэтому мы должны обмануть ее, чтобы позволить пользователю уменьшить представление вниз.

Очень хорошо тогда. Когда вы создаете представление прокрутки, вы вставляете масштабируемое представление в масштабе 1,0. Это может быть в вашем viewDidLoad, например (sv - это представление прокрутки):

[self addNewScalableViewAtScale: 1.0];
sv.minimumZoomScale = MIN;
sv.maximumZoomScale = MAX;
self->oldScale = 1.0;
sv.delegate = self;

Затем в вашем scrollViewDidEndZooming: вы делаете то же самое, но компенсируете изменение масштаба:

UIView* v = [sv viewWithTag:999];
[v removeFromSuperview];
CGFloat newscale = scale * self->oldScale;
self->oldScale = newscale;
[self addNewScalableViewAtScale:newscale];
sv.minimumZoomScale = MIN / newscale;
sv.maximumZoomScale = MAX / newscale;
2 голосов
/ 28 июля 2009

Обратите внимание, что решение, предоставленное Брэдом, имеет одну проблему: если вы продолжаете программно увеличивать масштаб (скажем, по событию двойного нажатия) и позволяете пользователю уменьшать масштаб вручную (через некоторое время), через некоторое время разница между истинным масштабом и масштаб, который отслеживает UIScrollView, станет слишком большим, поэтому previousScale в какой-то момент выйдет из-под точности поплавка, что в итоге приведет к непредсказуемому поведению

0 голосов
/ 29 июня 2018

Swift 4. * и Xcode 9,3

Установка масштаба масштабирования scrollView на 0 вернет ваш scrollView в исходное состояние.

self.scrollView.setZoomScale(0.0, animated: true)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...