在UIScrollView中设置zoomScale动画时不需要的滚动

时间:2011-09-04 19:53:20

标签: iphone ios uiscrollview core-animation catiledlayer

执行摘要: 有时UIScrollView会对contentOffset的值进行不必要的更改,从而导致应用在正在查看的文档中显示错误的位置。不必要的更改与滚动视图zoomScale的动画更改一起发生。

详细信息: 我在CATiledLayer中使用UIScrollView缩小时遇到问题。 CATiledLayer包含pdf,当contentOffset在一定范围内时,当我缩小时,contentOffset会在缩放发生之前发生变化(这就是错误)。似乎在Apple的代码中更改了contentOffset

为了说明问题,我修改了Apple的示例应用程序ZoomingPDFViewer。代码在github上:https://github.com/DirkMaas/ZoomingPDFViewer-bug

点按将使zoomScaleanimateWithDuration更改为0.5,从而缩小。如果UIScrollView的{​​{1}}小于约2700或大于5900,则contentOffset.y动画可以正常工作。如果在zoomScale介于这两个值之间时发生了点击,则contentOffset.y会跳转(非动画)到约2700,然后会出现contentOffset.y动画,但会在同时,这样当动画完成时,zoomScale就是它应该的位置。但跳跃从何而来?

例如,当点击屏幕时,说contentOffset.y是2000:contentOffset.y动画效果很好; zoomScale未被更改。

但是,如果点击屏幕时contentOffset.y为4000:contentOffset.y将跳转,不动画,大约2700,然后缩放和滚动将从该点开始并在同一时间发生时间。当动画完成时,看起来好像我们从4000直接缩放,所以我们最终在正确的位置,但行为是错误的。

关于用户界面的说明:

  • 文本可以正常方式垂直滚动
  • 可以通过正常方式捏合来放大和缩小文本
  • 单击将导致contentOffset.y设置为0.5;这个变化是动画的

我注意到如果zoomScale大于0.5,则跳跃不会那么大。此外,如果我使用zoomScale而不是setZoomScale:animated:,则错误消失,但我无法使用它,因为我需要链接动画。

以下是我所做的总结(github中的代码包含这些更改):

  • 从中下载ZoomingPDFViewer http://developer.apple.com/library/ios/#samplecode/ZoomingPDFViewer/Introduction/Intro.html并在XCode中打开它
  • 更改了构建设置|架构|基础SDK到最新的iOS(iOS 4.3)改变了Build Settings | GCC 4.2 - 语言|编译源于Objective-C ++
  • 从项目中删除了TestPage.pdf
  • 将“whoiam 5 24 cropped 3-2.pdf”添加到该项目中
  • animateWithDuration添加到PDFScrollView *scrollView;
  • 更改了ZoomingPDFViewerViewController中的loadView以初始化ZoomingPDFViewerViewController而不是scrollView
  • 在PDFScrollview.m中将svviewDidLoadhandleTapFrom:recognizer添加到zoomOut
  • 评论了ZoomingPDFViewerViewControllerscrollViewDidEndZooming:withView:atScale,因为他们在图片背景中填写了分散注意力的问题

非常感谢你对我的支持,以及任何和所有的帮助!

5 个答案:

答案 0 :(得分:8)

关于缩放的一个最棘手的事情是,它总是发生在一个叫做锚点的点附近。我认为理解它的最好方法是想象一个坐标系统层叠在另一个上面。说A是你的外部坐标系,B是内部(B是滚动视图)。当B的偏移为(0,0)且标度为1.0时,则点B(0,0)对应于A(0,0),并且通常B(x,y)= A(x,y) )。

此外,如果B的偏移是(xOff,yOff)则B(x,y)= A(x-xOff,y-yOff)。同样,这仍然假设缩放比例是1.0。

现在,让偏移量再次为(0,0)并想象当您尝试缩放时会发生什么。在缩放时屏幕上必须有一个点不移动,并且每个其他点从该点向外移动。这就是锚点定义的内容。如果您的锚点是(0,0),则左下角点将保持固定,而所有其他点向上和向右移动。在这种情况下,偏移量保持不变。

如果你的锚点是(0.5,0.5)(锚点被标准化,即0到1,所以0.5是中途),那么中心点保持固定而所有其他点向外移动。这意味着必须改变偏移以反映这一点。如果它在纵向模式的iPhone上,并且缩放到2.0,则锚点x值将移动屏幕宽度的一半,320/2 = 160。

屏幕上滚动视图内容视图的实际位置由偏移量和锚点定义。因此,如果您只是更改下面的图层锚点而不对偏移进行相应的更改,您将看到视图似乎跳转到其他位置,即使偏移量相同。

我猜这是潜在的问题。在为缩放设置动画时,Core Animation必须选择一个新的锚点,以便缩放“看起来”正确。这也将改变偏移量,以便屏幕上的视图实际可见区域不会跳跃。尝试在整个过程中的不同时间记录锚点的位置(它是在您使用Views“layer”属性访问的任何View的基础CALayer上定义的。)

另外,请参阅文档here了解漂亮的图片,并且可能比我在这里给出的情况描述要好得多:)

最后,这是我在应用中使用的一段代码,用于更改图层的锚点,而不会在屏幕上移动。

-(CGPoint)setNewAnchorPointWithoutMoving:(CGPoint)newAnchor {
    CGPoint currentAnchor = CGPointMake(self.anchorPoint.x * self.contentSize.width,
                                        self.anchorPoint.y * self.contentSize.height);

    CGPoint offset = CGPointMake((1 - self.scale) * (currentAnchor.x - newAnchor.x),
                                 (1 - self.scale) * (currentAnchor.y - newAnchor.y));


    self.anchorPoint = CGPointMake(newAnchor.x / self.contentSize.width,
                                   newAnchor.y / self.contentSize.height);

    self.position = CGPointMake(self.position.x + offset.x, self.position.y + offset.y);

    return offset;
}

答案 1 :(得分:3)

我一直在玩你发布的代码,我想我已经发现了正在发生的事情。当您执行代码中的块动画时:

[UIView animateWithDuration:4.0
                 animations:^ {
                     self.scrollView.zoomScale = 0.5;
                 }
                 completion:nil];

动画块实际上是在动画开始时调用的。核心动画内部处理层,使其看起来像实际移动。开发中心有一个list of Animatable Properties,缩放比例不在其中。

当zoomScale更改时,scrollView会自动更新其contentOffset。因此,当动画开始时,您看到的跳转是针对新的zoomScale调整的contentOffset。然后将图层设置为适当的缩放比例,但目标contentOffset(即缩放结束时contentOffset应该是什么)已经在缩放开始时设置。

这也是为什么缩放看起来像是以屏幕上的一个点为中心的原因。您要么必须使用setZoomScale:animated:或者自己动画图层和偏移量...

答案 2 :(得分:2)

您应该使用scrollViewDidEndZooming:withView:atScale来通知您缩放何时完成。否则我相信您将不得不删除UIScrollView属性zoomScale的使用,而是完全手动实现缩放,例如http://jonathanwatmough.com/2008/12/implementing-tap-to-zoom-in-uiscrollview-on-an-iphone/

答案 3 :(得分:2)

当我的viewForZoomingInScrollView函数直接返回scrollview时,我有许多错误进行缩放。

我在scrollView中设置了包含所有内容并在viewForZoomingInScrollView中返回的视图后,许多奇怪的行为都消失了。也许这也可以解决你的问题:

-(void) someIntializationFunction {
    [myScrollView addSubView:self.globalContainer];
    // Now had everything in the container and not in the scrollView directly
    ....
}

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

另一件事,我见过zoomToRect:函数比改变scrollView的zoomScale更可靠。这也有帮助,即使你有更多的计算要做......

答案 4 :(得分:0)

我认为你必须在每个滚动事件上自己操纵contentInset和/或contentOffset。操纵锚点无济于事。看看this question和我的答案。