import UIKit import SwiftUI /// UIKit 触控捕获视图:将触摸转换为相对坐标(0.0~1.0)的远端输入指令。 /// 与 Android 端 RemoteTouchView 行为一致: /// - 实时转发原始 MotionEvent(DOWN=0 / UP=1 / MOVE=2 / CANCEL=3),远端注入实现"跟手" /// - 手指抬起时若判定为滑动(时长 >= 200ms 或位移 >= 2%),额外发送一条 SWIPE 指令 final class RemoteTouchView: UIView { /// Android MotionEvent 动作常量 enum MotionAction { static let down: Int32 = 0 static let up: Int32 = 1 static let move: Int32 = 2 static let cancel: Int32 = 3 } var onMotionEvent: ((Int32, Double, Double) -> Void)? var onSwipe: ((Double, Double, Double, Double, Int64) -> Void)? private var startX: Double = 0 private var startY: Double = 0 private var touchStartTime: TimeInterval = 0 override init(frame: CGRect) { super.init(frame: frame) isMultipleTouchEnabled = false backgroundColor = .clear } required init?(coder: NSCoder) { super.init(coder: coder) isMultipleTouchEnabled = false backgroundColor = .clear } private func relativePoint(of touch: UITouch) -> (Double, Double) { let p = touch.location(in: self) let w = max(bounds.width, 1) let h = max(bounds.height, 1) let relX = min(max(Double(p.x / w), 0), 1) let relY = min(max(Double(p.y / h), 0), 1) return (relX, relY) } override func touchesBegan(_ touches: Set, with event: UIEvent?) { guard let touch = touches.first else { return } let (x, y) = relativePoint(of: touch) startX = x startY = y touchStartTime = Date().timeIntervalSince1970 onMotionEvent?(MotionAction.down, x, y) } override func touchesMoved(_ touches: Set, with event: UIEvent?) { guard let touch = touches.first else { return } let (x, y) = relativePoint(of: touch) onMotionEvent?(MotionAction.move, x, y) } override func touchesEnded(_ touches: Set, with event: UIEvent?) { guard let touch = touches.first else { return } let (x, y) = relativePoint(of: touch) onMotionEvent?(MotionAction.up, x, y) let duration = Int64((Date().timeIntervalSince1970 - touchStartTime) * 1000) let dx = abs(x - startX) let dy = abs(y - startY) // 非轻点:补发 SWIPE 高层指令(与 Android 端一致) if !(duration < 200 && dx < 0.02 && dy < 0.02) { onSwipe?(startX, startY, x, y, duration) } } override func touchesCancelled(_ touches: Set, with event: UIEvent?) { guard let touch = touches.first else { return } let (x, y) = relativePoint(of: touch) onMotionEvent?(MotionAction.cancel, x, y) } } /// SwiftUI 包装:把 UIKit 触控层嵌入 SwiftUI 视图树 struct TouchOverlay: UIViewRepresentable { let onMotionEvent: (Int32, Double, Double) -> Void let onSwipe: (Double, Double, Double, Double, Int64) -> Void func makeUIView(context: Context) -> RemoteTouchView { let view = RemoteTouchView() view.onMotionEvent = onMotionEvent view.onSwipe = onSwipe return view } func updateUIView(_ uiView: RemoteTouchView, context: Context) { uiView.onMotionEvent = onMotionEvent uiView.onSwipe = onSwipe } }