执行摘要:
有时UIScrollView
会对contentOffset
的值进行不必要的更改,从而导致应用在正在查看的文档中显示错误的位置。不必要的更改与滚动视图zoomScale
的动画更改一起发生。
详细信息:
我在CATiledLayer
中使用UIScrollView
缩小时遇到问题。 CATiledLayer
包含pdf,当contentOffset
在一定范围内时,当我缩小时,contentOffset
会在缩放发生之前发生变化(这就是错误)。似乎在Apple的代码中更改了contentOffset
。
为了说明问题,我修改了Apple的示例应用程序ZoomingPDFViewer。代码在github上:https://github.com/DirkMaas/ZoomingPDFViewer-bug
点按将使zoomScale
将animateWithDuration
更改为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中的代码包含这些更改):
animateWithDuration
添加到PDFScrollView *scrollView;
类ZoomingPDFViewerViewController
中的loadView
以初始化ZoomingPDFViewerViewController
而不是scrollView
sv
,viewDidLoad
和handleTapFrom:recognizer
添加到zoomOut
ZoomingPDFViewerViewController
和scrollViewDidEndZooming:withView:atScale
,因为他们在图片背景中填写了分散注意力的问题非常感谢你对我的支持,以及任何和所有的帮助!
答案 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和我的答案。