如何跟踪缩放的 UIScrollView?

Posted

技术标签:

【中文标题】如何跟踪缩放的 UIScrollView?【英文标题】:How to track a zooming UIScrollView? 【发布时间】:2014-11-04 17:11:57 【问题描述】:

我有一个缩放 UIScrollView 和一个非缩放覆盖视图,我在上面设置动画标记。这些标记需要跟踪 UIScrollView 的某些内容的位置(类似于在您平移和缩放时放置的图钉需要跟踪地图上某个点的方式)。

我通过触发覆盖视图的更新来响应 UIScrollView 的layoutSubviews。这很有效,并且覆盖在缩放和平移时完美地跟踪。

但是当捏合手势结束时,UIScrollView 会自动执行最终动画,并且覆盖视图在此动画期间不同步。

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

此测试的完整 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】:

UIScrollViewscrollViewDidZoom: 发送到其动画块中的代理,如果它在捏合结束时“弹回”到其最小或最大缩放。如果zoomBouncing 为真,则更新scrollViewDidZoom: 中的叠加层框架。如果您使用自动布局,请致电 layoutIfNeeded

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

演示:

固定示例项目:https://github.com/mayoff/ScrollviewZoomTrackTest

【讨论】:

整洁!我的印象是scrollViewDidZoom: 只被调用一次,而不是在动画的每一步。非常感谢您的帮助。 顺便说一句,我向 Apple 开发人员技术支持发送了完全相同的问题和相同的项目和动画屏幕截图,这是他们的回复:“我们的工程师已经审查了您的请求并确定这将是最好作为错误报告处理”。堆栈 1:Apple 支持 0 最后说明:代理的scrollViewDidScroll: 也需要类似的实现,因为在某些情况下,动画不是缩放,而是使滚动视图重新居中的平移。再次非常感谢 Rob 的精彩回答! 这个答案为我节省了很多时间,我遇到了与 OP 非常相似的问题,并且还尝试使用 CADisplayLink 来跟踪 presentationLayer问题。 @Clafou:scrollViewDidZoom: 仍然只在反弹动画开始时调用一次。但是,从scrollViewDidZoom: 调用layoutIfNeeded 将使布局调整动画与反弹动画同步发生。

以上是关于如何跟踪缩放的 UIScrollView?的主要内容,如果未能解决你的问题,请参考以下文章

iPhone:如何在捏缩放uiscrollview时重绘子视图

如何保持 UIscrollview 框架固定但动态范围

UIScrollView 后面如何与 UIView 交互

如何使用 UIScrollView 捕捉缩放?

如何在 UICollectionViewCell 内缩放 UIScrollView?

如何使在Swift中使用UIScrollView进行滚动和缩放