2014-11-04 94 views
3

我有一个缩放的UIScrollView,和一个非缩放的叠加视图,我在这个视图上设置了动画标记。这些标记需要跟踪UIScrollView的一些内容的位置(类似于在平移和缩放时丢弃的引脚需要在地图上追踪地点的方式)。如何跟踪缩放UIScrollView?

我通过响应UIScrollView的layoutSubviews触发重叠视图的更新操作。这是有效的,并且在缩放和平移时覆盖层完美地追踪。

但当捏手势结束的UIScrollView自动执行最后的动画,并且该覆盖图是不同步的该动画的持续时间。

我做了一个简化的项目来隔离这个问题。 UIScrollView包含一个橙色正方形,覆盖视图在这个橙色正方形的框架周围显示一个2像素的红色轮廓。正如你在下面看到的那样,红色轮廓总是移动到它应该在的位置,除非触摸结束后的一小段时间,当它明显跳到橙色方块的最终位置时。

red line around a zooming rectangle

本次测试的完整的Xcode项目可以在这里找到:https://github.com/Clafou/ScrollviewZoomTrackTest但所有的代码是在如下所示的两个文件:

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 
    } 
} 

OverlaysView.swift

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() 
     } 
    } 
} 

其中我试过的东西是使用CADisplayLinkpresentationLayer这允许我为覆盖层设置动画,但我从presentationLayer获得的坐标稍微落后于实际的UIScrollView,所以这看起来仍然不正确。我认为正确的做法是将我的覆盖更新与系统创建的UIScrollView动画绑定,但是迄今为止我还没有取得成功。

如何更新验证码总是跟踪UIScrollView的缩放内容?

回答

4

UIScrollView在其动画块中将scrollViewDidZoom:发送给其委托人,如果它在收缩结束时“弹回”回到其最小或最大缩放比例。如果zoomBouncing为true,则更新scrollViewDidZoom:中覆盖图的帧。如果您使用自动布局,请致电layoutIfNeeded

scrollViewDidZoom:变焦反弹动画过程中只调用一次调整您的框架或致电layoutIfNeeded将确保这些变化与变焦反弹沿着动画,从而保持它们完全同步。

演示:

demo of correct animation

固定样本项目:https://github.com/mayoff/ScrollviewZoomTrackTest

+0

整洁!我的印象是'scrollViewDidZoom:'只被调用一次,而不是在动画的每一步。我非常感谢你的帮助。 – Clafou 2014-11-07 13:25:32

+0

顺便说一下,我向Apple Developer Technical Support发送了同一个项目和动画截图的完全相同的问题,这是他们的回应:“我们的工程师已审核您的请求,并确定这最好作为错误报告处理” 。堆栈1:Apple支持0 – Clafou 2014-11-07 13:30:31

+0

最后说明:代理的'scrollViewDidScroll:'也需要类似的实现,因为在某些情况下,动画不是缩放而是重新重新调入滚动视图的平移。并且非常感谢你Rob的真棒回答! – Clafou 2014-11-07 17:21:58