【问题标题】:In iOS 4.0, why does UIScrollView zoomToRect:animated: not trigger the scrollViewDidScroll or scrollViewDidZoom delegates while animating?在 iOS 4.0 中,为什么 UIScrollView zoomToRect:animated: 在动画时不触发 scrollViewDidScroll 或 scrollViewDidZoom 委托?
【发布时间】:2011-04-28 22:57:01
【问题描述】:

我需要密切监视滚动视图的比例,以便我可以根据滚动视图的动画缩放来更新内容视图的元素(管理多个 CALayers 的子视图)。

在 iOS 3.1 上,一切正常,我使用 zoomToRect:animated:UIScrollViewDelegatescrollViewDidScroll: 消息 在动画发生时重复调用,让我根据实际缩放更新子视图元素。

iOS 4.0 上的相同代码不会表现出相同的行为。当我调用 zoomToRect:animated: 时,代表(scrollViewDidScroll:scrollViewDidZoom)只会被调用 一次,这使我的子元素去同步,直到动画完成。事实上,子元素会立即跳跃,然后被缩放动画赶上,直到一切都在正确的位置。就好像动画没有拾取子视图CALayers 上的修改。

我曾尝试使用动画块手动制作动画,但情况相同,没有渐进式回调调用。我也尝试过 KVO,但我不清楚如何利用 UIScrollView 管理的动画。

在 iOS 4 上是否有一种解决方法可以让我在 UIScrollView 缩放动画时将缩放值推送到我的子视图?

【问题讨论】:

    标签: ios ios4 uiscrollview uiscrollviewdelegate


    【解决方案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 滴答声。它也运行核心动画,所以它会以相同的间隔被踢:

    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) {}];
      

      这应该在同一动画上下文中设置子视图的帧属性,因此它们应该由操作系统一起设置动画。

      另请参阅此问题的答案:How to make UIScrollView send scrollViewDidScroll messages during animations

      【讨论】:

        【解决方案3】:

        在调用zoomToRect:animated: 后注册UIScrollViewzoomScale 属性的KVO。

        [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

        【讨论】:

        • 提出了一个非常古老的答案,但我似乎根本无法让它发挥作用。我将自己注册为 @"zoomScale" 的关键路径的 scrollView 观察者,但 observeValueForKeyPath:ofObject:change:context: 永远不会被调用。
        • Adam Mika,你为什么不使用 UIScrollViewDelegate 协议中的 scrollViewDidZoom developer.apple.com/library/ios/#documentation/UIKit/Reference/…(虽然是 iOS 3.2+)
        • 已经很久了,但我需要对此投反对票,因为它不起作用。永远不会调用 observeValueForKeyPath。不是在缩放结束时也不是在缩放发生时。
        • 在这里也不起作用 - 但我需要它,因为在动画期间不会调用 scrollViewDidZoom
        【解决方案4】:

        更新动画块(或开始/提交动画)中的所有子视图是否可行? 比如:

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

        【讨论】:

        • 我已经尝试过这种方法,但问题是我需要在运行缩放动画时访问缩放级别的当前值,这是不可用的。
        • 您能否设置一个计时器,在缩放动画进行时触发多次并在那里进行计算?
        猜你喜欢
        • 2014-05-11
        • 1970-01-01
        • 2012-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-11
        • 2011-06-18
        • 1970-01-01
        相关资源
        最近更新 更多