import AVFoundation import SwiftUI import UIKit /// 与安卓端 OralTrainerPlayerView 对齐的播放区: /// - 单击:播放 / 暂停 /// - 左划 / 右划:上一句 / 下一句(无句边界时回退为快退 / 快进) /// - 上划 / 下划:显示 / 隐藏字幕层(由 SwiftUI 横屏页接收) /// - 长按:按触摸点水平位置变速(左慢右快),松手恢复原速 struct PlayerSurfaceView: UIViewRepresentable { let controller: PlayerController var onVerticalSwipe: ((Bool) -> Void)? func makeUIView(context: Context) -> VideoSurfaceView { let view = VideoSurfaceView() view.playerLayer.player = controller.player view.controller = controller view.onVerticalSwipe = onVerticalSwipe return view } func updateUIView(_ uiView: VideoSurfaceView, context: Context) { uiView.playerLayer.player = controller.player uiView.onVerticalSwipe = onVerticalSwipe } } final class VideoSurfaceView: UIView { weak var controller: PlayerController? var onVerticalSwipe: ((Bool) -> Void)? override class var layerClass: AnyClass { AVPlayerLayer.self } var playerLayer: AVPlayerLayer { layer as! AVPlayerLayer } private let minSwipeDistance: CGFloat = 48 private let minSwipeVelocity: CGFloat = 160 private let longPressTimeout: TimeInterval = 0.5 private var tapRecognizer: UITapGestureRecognizer! private var panRecognizer: UIPanGestureRecognizer! private var longPressRecognizer: UILongPressGestureRecognizer! override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .black playerLayer.videoGravity = .resizeAspect tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap)) panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan)) panRecognizer.minimumNumberOfTouches = 1 panRecognizer.maximumNumberOfTouches = 1 longPressRecognizer = UILongPressGestureRecognizer( target: self, action: #selector(handleLongPress) ) longPressRecognizer.minimumPressDuration = longPressTimeout tapRecognizer.require(toFail: panRecognizer) tapRecognizer.require(toFail: longPressRecognizer) panRecognizer.require(toFail: longPressRecognizer) addGestureRecognizer(tapRecognizer) addGestureRecognizer(panRecognizer) addGestureRecognizer(longPressRecognizer) isUserInteractionEnabled = true } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @objc private func handleTap() { controller?.handleGesture(.singleTap) } @objc private func handlePan(_ recognizer: UIPanGestureRecognizer) { guard recognizer.state == .ended || recognizer.state == .cancelled else { return } let translation = recognizer.translation(in: self) let velocity = recognizer.velocity(in: self) let dx = translation.x let dy = translation.y if abs(dy) > abs(dx), abs(dy) >= minSwipeDistance, abs(velocity.y) >= minSwipeVelocity { onVerticalSwipe?(dy < 0) return } guard abs(dx) >= abs(dy), abs(dx) >= minSwipeDistance, abs(velocity.x) >= minSwipeVelocity else { return } controller?.handleGesture(dx > 0 ? .swipeRight : .swipeLeft) } @objc private func handleLongPress(_ recognizer: UILongPressGestureRecognizer) { let location = recognizer.location(in: self) switch recognizer.state { case .began: controller?.beginLongPressSpeed(atX: location.x, width: bounds.width) case .changed: controller?.updateLongPressSpeed(atX: location.x, width: bounds.width) case .ended, .cancelled: controller?.endLongPressSpeed() default: break } } }