Как мне выполнить сброс после увеличения UIScrollView?
У меня есть график, нарисованный внутри UIScrollView
, Это один большой UIView
используя пользовательский подкласс CATiledLayer
как его слой.
Когда я увеличиваю и уменьшаю масштаб UIScrollView
Я хочу, чтобы график динамически изменял размеры, как это происходит, когда я возвращаю график из viewForZoomingInScrollView
, Однако график перерисовывается при новом уровне масштабирования, и я хочу сбросить масштаб преобразования до 1x1, чтобы при следующем увеличении пользователя преобразование начиналось с текущего представления. Если я сбросить преобразование в личность в scrollViewDidEndZooming
Работает в симуляторе, но кидает EXC_BAD_ACCSES
на устройстве.
Это даже не решает проблему полностью на симуляторе, потому что в следующий раз, когда пользователь увеличивает масштаб, преобразование сбрасывает себя к тому уровню масштабирования, на котором он был, и так выглядит, например, если я был увеличен до 2x, это внезапно в 4x. Когда я заканчиваю масштабирование, оно заканчивается в правильном масштабе, но сам факт масштабирования выглядит плохо.
Итак, сначала: как я могу позволить графу перерисовывать себя в стандартном масштабе 1x1 после масштабирования, и как у меня плавное масштабирование во всем?
Редактировать: Новые выводы Похоже, ошибка "[CALayer retainCount]: message sent to deallocated instance
"
Я никогда не освобождаю никакие слои самостоятельно. Раньше я даже не удалял ни взгляды, ни что-либо еще. Эта ошибка возникала при увеличении, а также при повороте. Если я удаляю объект перед вращением и повторно добавляю его после, это не выдает исключение. Это не вариант для увеличения.
7 ответов
Я не могу помочь вам с сбоем, кроме как сказать вам, чтобы проверить и убедиться, что вы случайно не выпускаете автоматически представление или слой где-то в вашем коде. Я видел, как симулятор обрабатывает время авто-релизов иначе, чем на устройстве (чаще всего, когда задействованы потоки).
Масштабирование представления является проблемой с 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.
Спасибо всем предыдущим ответам, и вот мое решение.
Реализуйте методы 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
- установить MaximumZoomScale и минимальный ZoomScale перед
- создайте предыдущую переменную экземпляра Scale и установите для нее значение 1.0f
У меня работает отлично.
БР, Евгений.
Я просто хотел сбросить при загрузке масштаб масштабирования по умолчанию, потому что когда я его масштабирую, у scrollview будет тот же масштаб масштабирования в следующий раз, пока он не будет освобожден.
-(void) viewWillAppear:(BOOL)animated {
[self.scrollView setZoomScale:1.0f];
}
Поменял игру.
У меня есть подробное обсуждение того, как (и почему) работает масштабирование UIScrollView на http://github.com/andreyvit/ScrollingMadness/.
(Ссылка также содержит описание того, как программно масштабировать UIScrollView, как эмулировать пейджинг в стиле фотоархива + масштабирование + прокрутка, пример проекта и класс ZoomScrollView, который включает в себя часть магии масштабирования.)
Цитата:
UIScrollView не имеет понятия "текущий уровень масштабирования", потому что у каждого подпредставления, которое он содержит, может быть свой собственный текущий уровень масштабирования. Обратите внимание, что в UIScrollView нет поля для сохранения текущего уровня масштабирования. Однако мы знаем, что кто-то сохраняет этот уровень масштабирования, потому что, если вы ущипните масштабирование подпредставления, затем сбросите его преобразование до CGAffineTransformIdentity, а затем снова укажете, вы заметите, что предыдущий уровень масштабирования подпредставления был восстановлен.
Действительно, если вы посмотрите на разборку, это UIView, который хранит свой собственный уровень масштабирования (внутри объекта UIGestureInfo, на который указывает поле _gestureInfo). Он также имеет набор хороших недокументированных методов, таких как zoomScale
а также setZoomScale:animated:
, (Имейте в виду, у него также есть куча методов, связанных с вращением, может быть, скоро мы получим поддержку жестов вращения.)
Однако, если мы создадим новый UIView только для масштабирования и добавим наш реальный масштабируемый вид как его дочерний элемент, мы всегда начнем с уровня масштабирования 1.0. Моя реализация программного масштабирования основана на этом приеме.
Swift 4.* и Xcode 9.3
Установка масштаба масштабирования scrollView в 0 вернет ваш scrollView в исходное состояние.
self.scrollView.setZoomScale(0.0, animated: true)
Довольно надежный подход, соответствующий iOS 3.2 и 4.0 и более поздним версиям, заключается в следующем. Вы должны быть готовы предоставить масштабируемое представление (главное подпредставление представления прокрутки) в любом запрошенном масштабе. Затем в scrollViewDidEndZooming:
Вы удалите размытую трансформированную по масштабу версию этого вида и замените ее новым видом, нарисованным в новом масштабе.
Тебе понадобится:
Ивар для поддержания предыдущего масштаба; давайте назовем это oldScale
Способ определения масштабируемого представления, как для
viewForZoomingInScrollView:
и дляscrollViewDidEndZooming:
; здесь я собираюсь применить тег (999)Метод, который создает масштабируемое представление, маркирует его и вставляет в представление прокрутки (здесь я назову его
addNewScalableViewAtScale:
). Помните, что он должен делать все в соответствии с масштабом - он изменяет размеры представления и его подпредставлений или чертежа, а также подбирает размер содержимого представления прокрутки для соответствия.Константы для минимального масштабирования и максимального масштабирования. Они необходимы, потому что, когда мы заменим масштабированное представление более подробной увеличенной версией, система будет думать, что текущий масштаб равен 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
в какой-то момент выпадет из точности поплавка, что в конечном итоге приведет к непредсказуемому поведению