Ios 如何跟踪缩放的UIScrollView?
我有一个缩放的UIScrollView和一个非缩放的覆盖视图,我在上面设置标记动画。这些标记需要跟踪UIScrollView中某些内容的位置(类似于平移和缩放时放置的图钉跟踪地图上某个点的方式) 我通过触发覆盖视图的更新来响应UIScrollView的Ios 如何跟踪缩放的UIScrollView?,ios,swift,uiscrollview,zooming,caanimation,Ios,Swift,Uiscrollview,Zooming,Caanimation,我有一个缩放的UIScrollView和一个非缩放的覆盖视图,我在上面设置标记动画。这些标记需要跟踪UIScrollView中某些内容的位置(类似于平移和缩放时放置的图钉跟踪地图上某个点的方式) 我通过触发覆盖视图的更新来响应UIScrollView的布局子视图。这是可行的,并且在缩放和平移时,叠加可以完美地跟踪 但是,当收缩手势结束时,UIScrollView会自动执行最终动画,并且覆盖视图在此动画期间不同步 我做了一个简化的项目来隔离这个问题。UIScrollView包含一个橙色正方形,覆盖
布局子视图
。这是可行的,并且在缩放和平移时,叠加可以完美地跟踪
但是,当收缩手势结束时,UIScrollView会自动执行最终动画,并且覆盖视图在此动画期间不同步
我做了一个简化的项目来隔离这个问题。UIScrollView包含一个橙色正方形,覆盖视图在该橙色正方形的框架周围显示一个2像素的红色轮廓。正如您在下面看到的,红色轮廓总是移动到它应该位于的位置,除了触摸结束后的一小段时间,当它明显地跳到橙色正方形的最终位置时
此测试的完整Xcode项目在此处可用:但所有代码都在以下两个文件中:
TrackedScrollView.swift:
class TrackedScrollView: UIScrollView {
@IBOutlet var overlaysView: UIView?
let square: UIView
required init(coder aDecoder: NSCoder) {
square = UIView(frame: CGRect(x: 50, y: 300, width: 300, height: 300))
square.backgroundColor = UIColor.orangeColor()
super.init(coder: aDecoder)
self.addSubview(square)
self.maximumZoomScale = 1
self.minimumZoomScale = 0.5
self.contentSize = CGSize(width: 500, height: 900)
self.delegate = self
}
override func layoutSubviews() {
super.layoutSubviews()
overlaysView?.setNeedsLayout()
}
}
extension TrackedScrollView: UIScrollViewDelegate {
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return square
}
}
class OverlaysView: UIView {
@IBOutlet var trackedScrollView: TrackedScrollView?
let outline: CALayer
required init(coder aDecoder: NSCoder) {
outline = CALayer()
outline.borderColor = UIColor.redColor().CGColor
outline.borderWidth = 2
super.init(coder: aDecoder)
self.layer.addSublayer(outline)
}
override func layoutSubviews() {
super.layoutSubviews()
if let trackedScrollView = self.trackedScrollView {
CATransaction.begin()
CATransaction.setDisableActions(true)
let frame = trackedScrollView.convertRect(trackedScrollView.square.frame, toView: self)
outline.frame = CGRectIntegral(CGRectInset(frame, -3, -3))
CATransaction.commit()
}
}
}
OverlyView.swift:
class TrackedScrollView: UIScrollView {
@IBOutlet var overlaysView: UIView?
let square: UIView
required init(coder aDecoder: NSCoder) {
square = UIView(frame: CGRect(x: 50, y: 300, width: 300, height: 300))
square.backgroundColor = UIColor.orangeColor()
super.init(coder: aDecoder)
self.addSubview(square)
self.maximumZoomScale = 1
self.minimumZoomScale = 0.5
self.contentSize = CGSize(width: 500, height: 900)
self.delegate = self
}
override func layoutSubviews() {
super.layoutSubviews()
overlaysView?.setNeedsLayout()
}
}
extension TrackedScrollView: UIScrollViewDelegate {
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return square
}
}
class OverlaysView: UIView {
@IBOutlet var trackedScrollView: TrackedScrollView?
let outline: CALayer
required init(coder aDecoder: NSCoder) {
outline = CALayer()
outline.borderColor = UIColor.redColor().CGColor
outline.borderWidth = 2
super.init(coder: aDecoder)
self.layer.addSublayer(outline)
}
override func layoutSubviews() {
super.layoutSubviews()
if let trackedScrollView = self.trackedScrollView {
CATransaction.begin()
CATransaction.setDisableActions(true)
let frame = trackedScrollView.convertRect(trackedScrollView.square.frame, toView: self)
outline.frame = CGRectIntegral(CGRectInset(frame, -3, -3))
CATransaction.commit()
}
}
}
在我尝试的方法中,我使用了CADisplayLink
和presentationLayer
,这允许我设置覆盖动画,但是我从presentationLayer
获得的坐标稍微落后于实际的UIScrollView,所以这看起来仍然不正确。我认为正确的方法是将我的覆盖更新绑定到系统创建的UIScrollView动画,但到目前为止,我还没有成功破解这个问题
如何更新此代码以始终跟踪UIScrollView的缩放内容?
UIScrollView
将scrollViewDidZoom:
发送到其动画块中的代理,前提是在收缩结束时该代理“反弹”回其最小或最大缩放。如果缩放
为真,则在scrollViewDidZoom:
中更新覆盖图的帧。如果您使用的是自动布局调用layoutifneed
scrollViewDidZoom:
仅在缩放反弹动画期间调用一次,但调整帧或调用LayoutFneedd
将确保这些更改与缩放反弹一起设置动画,从而使它们完全同步
演示:
固定样本项目:整洁!我的印象是,
scrollViewDidZoom:
只被调用一次,而不是在动画的每一步。我非常感谢您的帮助。顺便说一句,我向苹果开发人员技术支持发送了与同一项目和动画截图完全相同的问题,这是他们的回答:“我们的工程师已经审查了您的请求,并确定这最好作为错误报告处理。”。堆栈1:Apple支持0最后注意:代理的scrollViewDidScroll:
也需要类似的实现,因为在某些情况下,动画不是缩放,而是重新居中滚动视图的平移。再次非常感谢罗布给我的精彩回答!这个答案为我节省了很多时间,我遇到了一个与OP非常相似的问题,并且还尝试使用CADisplayLink
跟踪出现相同问题的presentationLayer
@Clafou:scrollViewDidZoom:
在反弹动画开始时仍然只调用一次。但是,从scrollViewDidZoom:
调用LayoutFneed
将使布局调整动画与反弹动画同步。