Вопрос

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

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

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

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

Редактировать: Новые выводы Ошибка, по-видимому, заключается в "[CALayer retainCount]: message sent to deallocated instance"

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

Это было полезно?

Решение

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

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

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

В качестве обходного пути я использую подкласс 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, минуя настройку в обычном методе -scrollViewDidEndZooming:withView:atScale:. Похоже, что это обеспечивает правильное поведение при масштабировании, позволяя рисовать четкое изображение при завершении масштабирования.

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

Другие советы

Спасибо за все предыдущие ответы, и вот мое решение.
Реализовать 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];
}
  • втм это мой подкласс UIView
  • tmvScrollView — выход в UIScrollView
  • установленный Максимальный масштаб и Минимальный масштаб до того, как
  • Создать Предыдущий масштаб создайте переменную экземпляра и установите ее значение равным 1.0f

У меня это работает идеально.

БР, Юджин.

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

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

Изменил игру.

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

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

Цитата:

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

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

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

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

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

  • ivar для сохранения прежнего масштаба;давайте назовем это Олдскейлом

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

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

  • Константы для minimumZoomScale и 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;

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

Swift 4. * и Xcode 9.3

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

self.scrollView.setZoomScale(0.0, animated: true)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top