10

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

详细信息:CATiledLayer在使用UIScrollView. CATiledLayer保存一个pdf,当在contentOffset一定范围内时,当我缩小时,contentOffset在缩放发生之前改变了(这就是错误)。contentOffset似乎在 Apple 的代码中有所更改。

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

点击将导致zoomScale更改为 0.5,使用animateWithDuration,从而缩小。如果UIScrollView'scontentOffset.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 直接放大,所以我们最终在正确的位置,但行为是错误的。

关于 UI 的说明:

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

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

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

  • 从http://developer.apple.com/library/ios/#samplecode/ZoomingPDFViewer/Introduction/Intro.html下载 ZoomingPDFViewer 并在 XCode 中打开
  • 更改构建设置 | 架构 | Base SDK 到最新的 iOS (iOS 4.3) 更改了构建设置 | GCC 4.2 - 语言 | 根据 Objective-C++ 编译源代码
  • 从项目中删除了 TestPage.pdf
  • 将“whoiam 5 24 cropped 3-2.pdf”添加到项目中
  • 添加PDFScrollView *scrollView;ZoomingPDFViewerViewController课堂
  • 改为初始化loadView而不是ZoomingPDFViewerViewControllerscrollViewsv
  • 添加到PDFScrollview.m中viewDidLoad_handleTapFrom:recognizerzoomOutZoomingPDFViewerViewController
  • 注释掉了scrollViewDidEndZooming:withView:atScalescrollViewWillBeginZooming:withView:因为他们在图像背景中做的事情分散了手头的问题

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

4

5 回答 5

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 必须选择一个新的锚点,以便缩放“看起来”正确。这也将更改偏移量,以使屏幕上的视图实际可见区域不会跳转。尝试在整个过程中的不同时间记录锚点的位置(它在您使用视图“层”属性访问的任何视图的底层 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;
}
于 2011-09-14T20:55:48.390 回答
3

我一直在玩你发布的代码,我想我已经找到了发生了什么。当您在代码中执行块动画时:

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

动画块实际上在动画开始时被调用。Core Animation 在内部处理使它看起来像实际移动的层。Dev Center 上有一个Animatable Properties 列表,zoomScale 不在其中。

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

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

于 2011-09-22T00:00:26.013 回答
2

当我的 viewForZoomingInScrollView 函数直接返回滚动视图时,我遇到了很多缩放错误。

在我在包含所有内容的 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 更可靠。即使您需要进行更多计算,这也可能会有所帮助...

于 2011-09-19T22:57:03.293 回答
2

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

于 2011-09-15T17:24:41.050 回答
0

我认为您必须在每个滚动事件上自己操作 contentInset 和/或 contentOffset。操纵锚点将无济于事。看看这个问题和我的回答。

于 2013-10-25T20:57:24.810 回答