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

Dir*_*aas 9 iphone core-animation uiscrollview catiledlayer ios

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

详细信息: 与缩小,当我遇到问题CATiledLayerUIScrollView.该CATiledLayer持有PDF,并在contentOffset一定范围内,当我缩小,将contentOffset被改变(这是BUG)变焦发生之前.在contentOffset似乎在苹果的代码被改变.

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

点按将导致zoomScale更改为0.5,使用animateWithDuration,因此缩小.如果UIScrollView's contentOffset.y小于约2700或大于5900,则zoomScale动画效果正常.如果contentOffset.y在这两个值之间发生点击,contentOffset.y则会跳转(不动画)到约2700,然后zoomScale动画将发生,但同时会发生滚动,因此当动画完成时,contentOffset.y就是它的位置应该.但跳跃从何而来?

例如,说contentOffset.y屏幕被点击时是2000:zoomScale动画效果很好; contentOffset.y没有改变.

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

关于用户界面的说明:

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

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

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

  • http://developer.apple.com/library/ios/#samplecode/ZoomingPDFViewer/Introduction/Intro.html下载了ZoomingPDFViewer 并在XCode中打开它
  • 更改了构建设置| 架构| 基础SDK到最新的iOS(iOS 4.3)改变了Build Settings | GCC 4.2 - 语言| 编译源代码如Objective-C++
  • 从项目中删除了TestPage.pdf
  • 将"whoiam 5 24 cropped 3-2.pdf"添加到该项目中
  • 添加PDFScrollView *scrollView;ZoomingPDFViewerViewController
  • 改变loadViewZoomingPDFViewerViewController初始化scrollView,而不是sv
  • viewDidLoad,handleTapFrom:recognizerzoomOutZoomingPDFViewerViewController在PDFScrollview.m
  • 注释掉scrollViewDidEndZooming:withView:atScale,scrollViewWillBeginZooming:withView:因为他们在图像背景中做了一些东西,分散了手头的问题

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

dev*_*vid 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上定义).

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

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

-(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;
}
Run Code Online (Sandbox Code Playgroud)