UIScrollView双指放大时禁止拖动/禁止双指拖动/固定放大中心点

问题

做过iOS开发的都知道,UIScrollView是个好东西。UIView丢进去,即可自由拖拽、放大缩小。

但UIScrollView有个很讨厌的特性,双指放大时,画布也会随着两根手指拖动,无法固定放大中心点。

尝试

猜想,放大缩小的过程中,PanGesture拖动手势同时响应,造成画布放大同时可拖动的问题。

直接在放大开始前禁用pan手势。

    func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
        panGestureRecognizer.isEnabled = false
    }

可惜并没有什么卵用。实际上,pinch手势同时处理了画布的拖动和放大。

再一次尝试

如果在zoom的时候,手动控制contentOffset变化,将拖拽操作改变的contentOffset消除掉,是不是就能解决问题了呢?

只需要在开始放大时,记录下当前contentOffset大小,然后在scrollViewDidZoom中强行令它保持不变就可以了。

    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        contentOffset = pinchStartContentOffset
    }
    
    var pinchStartContentOffset: CGPoint = .zero
    
    func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
        pinchStartContentOffset = contentOffset
    }

现在,双指无法对UIScrollView造成拖动了。但UIScrollView会以contentView的左上角为中心放大缩小,整个图片往右下方跑,失去了以双指中点为中心放大的自然感。

思考

如果要满足以双指为中心缩放,只需修正contentOffset即可。

如图所示,UIScrollView的大小和屏幕大小相等,contentView小矩形希望以粉色点为中心点(中心点在放大前后,在屏幕上的位置不变),放大至紫色大矩形的大小。

前面观察到,当contentOffset不变时,UIScrollView默认的行为,是以contentView的左上角为原点缩放。缩放后,粉色中心点需要向左上↖️移动“小橙”的距离,即可满足中心点不动的要求。

解决

    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        guard pinchCenter != .zero else { return }
        contentOffset.x = pinchStartContentOffset.x + (pinchCenter.x + pinchStartContentOffset.x) * (zoomScale / pinchStartScale - 1)
        contentOffset.y = pinchStartContentOffset.y + (pinchCenter.y + pinchStartContentOffset.y) * (zoomScale / pinchStartScale - 1)
        pinchStartScale = zoomScale
        pinchStartContentOffset = contentOffset
    }
    
    var pinchCenter: CGPoint = .zero
    var pinchStartScale: CGFloat = 0
    var pinchStartContentOffset: CGPoint = .zero
    
    func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
        guard pinchGestureRecognizer?.numberOfTouches ?? 0 >= 2 else { return }
        guard
            let p1 = pinchGestureRecognizer?.location(ofTouch: 0, in: self),
            let p2 = pinchGestureRecognizer?.location(ofTouch: 1, in: self)
        else { return }
        pinchCenter = CGPoint(x: ((p1.x + p2.x) / 2 - contentOffset.x),
                              y: ((p1.y + p2.y) / 2 - contentOffset.y))
        pinchStartScale = zoomScale
        pinchStartContentOffset = contentOffset
    }

要注意的是,在使用UIScrollView的zoom(to:,等自动调整内容位置的方法时,pinchCenter必须手动设为zero。

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值