You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI实现类似Procreate双指平移画布且不冲突子手势的方案咨询

解决方案

提供两种可行方案,你可以根据项目支持的系统版本选择:

方案1:优化现有UIKit实现(兼容低版本iOS)

核心修改点

  • 实现手势识别代理,允许手势同时识别
  • 关闭手势默认的触摸取消逻辑,避免拦截子视图手势
  • 调整视图层级,避免覆盖子视图交互区域

修改后的TwoFingerNavigationView代码

// 新增遵循UIGestureRecognizerDelegate
struct TwoFingerNavigationView: UIViewRepresentable {
    var draggedCallback: ((CGPoint) -> Void)
    var dragEndedCallback: (() -> Void)

    class Coordinator: NSObject, UIGestureRecognizerDelegate {
        var draggedCallback: ((CGPoint) -> Void)
        var dragEndedCallback: (() -> Void)

        init(draggedCallback: @escaping ((CGPoint) -> Void),
             dragEndedCallback: @escaping (() -> Void)) {
            self.draggedCallback = draggedCallback
            self.dragEndedCallback = dragEndedCallback
        }

        @objc func dragged(gesture: UIPanGestureRecognizer) {
            if gesture.state == .ended {
                self.dragEndedCallback()
            } else {
                self.draggedCallback(gesture.translation(in: gesture.view))
            }
        }
        
        // 允许和其他手势同时识别
        func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
            return true
        }
        
        // 只有检测到双指的时候才触发当前手势,避免抢单指手势
        func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
            guard let pan = gestureRecognizer as? UIPanGestureRecognizer else { return false }
            return pan.numberOfTouches == 2
        }
    }

    func makeUIView(context: UIViewRepresentableContext<TwoFingerNavigationView>) -> UIView {
        let view = UIView(frame: .zero)
        let gesture = UIPanGestureRecognizer(target: context.coordinator,
                                             action: #selector(Coordinator.dragged))
        gesture.minimumNumberOfTouches = 2
        gesture.maximumNumberOfTouches = 2
        // 关键:不取消传递到子视图的触摸事件
        gesture.cancelsTouchesInView = false
        gesture.delaysTouchesBegan = false
        gesture.delegate = context.coordinator
        view.addGestureRecognizer(gesture)
        return view
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(draggedCallback: self.draggedCallback,
                           dragEndedCallback: self.dragEndedCallback)
    }

    func updateUIView(_ uiView: UIView,
                      context: UIViewRepresentableContext<TwoFingerNavigationView>) {
    }
}

调整使用方式

不要将导航视图放在ZStack的顶层,改为挂载到ZStack的背景上,避免覆盖子视图的交互:

ZStack {
    OtherView()
        .gesture(TapGesture().onEnded {
            // 此处手势可正常触发
        })
}
.background {
    TwoFingerNavigationView(draggedCallback: { translation in
        var newLocation = startLocation ?? location
        newLocation.x += (translation.x / scale)
        newLocation.y += (translation.y / scale)
        self.location = newLocation
    }, dragEndedCallback: {
        startLocation = location
    })
}

方案2:SwiftUI原生实现(支持iOS 16+)

iOS 16及以上版本可以直接使用原生手势的touchCount修饰符实现双指拖动,搭配simultaneousGesture避免和子视图手势冲突:

struct CanvasView: View {
    @State private var location: CGPoint = .zero
    @State private var startLocation: CGPoint?
    @State private var scale: CGFloat = 1.0
    
    var twoFingerDrag: some Gesture {
        DragGesture(minimumDistance: 0)
            .touchCount(2) // 限定双指触发
            .onChanged { value in
                var newLocation = startLocation ?? location
                newLocation.x += (value.translation.width / scale)
                newLocation.y += (value.translation.height / scale)
                self.location = newLocation
            }
            .onEnded { _ in
                startLocation = location
            }
    }
    
    var body: some View {
        ZStack {
            OtherView()
                .gesture(TapGesture().onEnded {
                    // 子视图手势正常工作
                })
        }
        // 用simultaneousGesture让当前手势和子视图手势同时识别,不会互相冲突
        .simultaneousGesture(twoFingerDrag)
    }
}

内容的提问来源于stack exchange,提问作者bob_mosh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 22:06:01