Подтвердить что ты не робот

В iOS 4.0 почему UIScrollView zoomToRect: анимированный: не запускать делегаты scrollViewDidScroll или scrollViewDidZoom во время анимации?

Мне нужно внимательно следить за масштабом представления прокрутки, чтобы я мог обновлять элементы представления контента (подматрица, управляющая несколькими CALayers) в соответствии с анимированным увеличением прокрутки.

В iOS 3.1 все работает так, как ожидалось, я использую zoomToRect: анимированный: и сообщение scrollViewDidScroll: сообщение UIScrollViewDelegate вызывается повторно во время анимации, позволяя мне обновлять элементы subview в соответствии с фактическое масштабирование.

Тот же самый код в iOS 4.0 не показывает того же поведения. Когда я вызываю zoomToRect: animated:, делегаты (scrollViewDidScroll: и scrollViewDidZoom) получают только вызов один раз, что делает мои дочерние элементы деинхронизированными до завершения анимации. Фактически, подэлементы сразу же прыгают, а затем увязаны анимацией масштабирования, пока все не окажется в правильном месте. Это как если бы анимация не собирала модификации на CALayers subviews.

Я пробовал анимацию вручную с блоками анимации, но ситуация такая же, никаких прогрессивных вызовов обратного вызова. Я также пробовал KVO, но мне непонятно, как я буду использовать анимацию, управляемую UIScrollView.

Есть ли обходной путь для iOS 4, который позволил бы мне подтолкнуть значение шкалы к моим подзонам, когда анимированная шкала UIScrollView?

4b9b3361

Ответ 1

Жестокий, но работает.

Определите некоторые свойства:

@property (nonatomic, strong) UIView *zoomedView;    
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic) CGFloat currentScale;

Сообщите UIScrollView, который UIView будет увеличен:

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
     return self.zoomedView;
}

Зарегистрируйте для CADisplayLink тиков. Он также запускает Core Animation, поэтому его будут пинать с одинаковыми интервалами:

self.displayLink  = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkTick)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

Проверить zoomedView presentationLayer для текущих значений.

- (void)displayLinkTick
{
    CALayer *zoomedLayer = self.zoomedView.layer.presentationLayer;
    CGFloat scale = zoomedLayer.transform.m11;

    if (scale != self.currentScale) {
         self.currentScale = scale; // update property

         // the scale has changed!
    }
}

Ответ 2

В IOS 4.0 анимация выполняется ОС - я предполагаю использовать максимально возможное аппаратное ускорение на основе GPU. В качестве недостатка этого становится сложнее оживить значения, полученные из другого (анимированного) значения. Как и в вашем случае, позиции подзонов, которые зависят от уровня масштабирования UIScrollView. Чтобы это произошло, вы должны настроить анимацию подвью, чтобы идти параллельно с анимацией масштабирования. Попробуйте что-то вроде:

[UIView animateWithDuration:0.5 delay:0
                    options:UIViewAnimationOptionLayoutSubviews
                 animations:^{
    theScrollView.zoomScale = zoomScale;
    // layout the subviews here
} completion:^(BOOL finished) {}];

Это должно задавать свойства фрейма субподробных объектов из одного и того же контекста анимации, и поэтому они должны быть анимированы вместе ОС.

См. также ответы на этот вопрос: Как заставить UIScrollView отправлять сообщения scrollViewDidScroll во время анимации

Ответ 3

Можно ли обновить все подзоны в блоке анимации (или начать /commitAnimation )? Что-то вроде:

[self zoomToRect:zoomRect animated:YES];
[UIView animateWithDuration:1.0
    animations:^ {
        // change your subviews
    }
];

Ответ 4

Зарегистрируйте для KVO свойства zoomScale UIScrollView после вызова zoomToRect:animated:.

[scrollView addObserver:self
            forKeyPath:@"zoomScale"
               options:(NSKeyValueObservingOptionNew |
                        NSKeyValueObservingOptionOld)
                context:NULL];

Затем реализуем этот метод:

- (void)observeValueForKeyPath:(NSString *)keyPath
              ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if ([keyPath isEqual:@"zoomScale"]) {
    // use the new zoomScale value stored
    // in the NSKeyValueChangeNewKey change
    // dictionary key  to resize your subviews
    }
    // be sure to call the super implementation
    // if the superclass implements it
    [super observeValueForKeyPath:keyPath
                ofObject:object
                 change:change
                 context:context];
}

Проверьте KVO documentation.