如何跟踪缩放UIScrollView?

时间:2014-11-04 17:11:58

标签: ios swift uiscrollview zoom caanimation

我有一个缩放的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的缩放内容?

1 个答案:

答案 0 :(得分:4)

UIScrollView将动态块中的scrollViewDidZoom:发送给其代理,如果它在收尾结束时“弹回”回到其最小或最大缩放。如果scrollViewDidZoom:为真,则在zoomBouncing中更新叠加层的框架。如果您正在使用自动布局调用layoutIfNeeded

scrollViewDidZoom:在缩放反弹动画期间仅被称为一次,但调整帧或调用layoutIfNeeded将确保这些更改与缩放反弹一起动画,从而保留它们完全同步。

演示:

demo of correct animation

修复了示例项目:https://github.com/mayoff/ScrollviewZoomTrackTest