SwiftUI中如何扩大AVPlayer视频的双击手势检测区域?
问题解决:双击视频仅右上角响应的修复方案
你的问题核心是手势绑定层级错误+VideoPlayer布局未填满父容器,导致双击检测区域被限制在极小范围。以下是具体修复方案:
原因分析
VideoPlayer是UIKit包装视图,默认不会自动填充整个父容器空间,你添加的.contentShape(Rectangle())仅作用于它自身的可视区域,而非整个feed流的视频占位区域- 手势直接绑定在
VideoPlayer上时,系统AVPlayer自带的交互控件(如右上角播放暂停按钮)会抢占部分区域的手势响应权
修复步骤
- 将手势绑定到外层
ZStack上,确保整个视频占位区域都能响应点击 - 给
VideoPlayer添加.frame(maxWidth: .infinity, maxHeight: .infinity)强制填充父容器 - 用
exclusiveGesture处理单双击的优先级冲突,避免两者互相干扰
修改后的完整代码
import SwiftUI import AVKit struct VideoPlayerView: View { @StateObject private var viewModel: VideoPlayerViewModel init(url: URL, isFeedView: Bool = true) { _viewModel = StateObject(wrappedValue: .init(url: url, isFeedView: isFeedView)) } var body: some View { ZStack { if let player: AVPlayer = viewModel.player { VideoPlayer(player: player) .frame(maxWidth: .infinity, maxHeight: .infinity) // 强制填充父容器 .onAppear { if viewModel.isFeedView { if let lastKnownTime = viewModel.lastKnownTime { player.seek(to: CMTime(seconds: lastKnownTime, preferredTimescale: 600)) } player.play() player.volume = 0 } } .onDisappear { viewModel.lastKnownTime = player.currentTime().seconds player.pause() } } } .maxSize() .contentShape(Rectangle()) // 给整个ZStack设置点击响应区域 // 用exclusiveGesture确保双击优先识别,避免单双击冲突 .gesture( TapGesture(count: 2) .onEnded { print("Double tap detected") viewModel.isFullScreen.toggle() } .exclusiveGesture( TapGesture() .onEnded { print("Single tap detected") viewModel.player?.volume = 1 } ) ) .fullScreenCover(isPresented: $viewModel.isFullScreen) { AVPlayerViewControllerRepresented(viewModel: viewModel) } } } class VideoPlayerViewModel: ObservableObject { @Published var player: AVPlayer? @Published var lastKnownTime: Double? @Published var isFullScreen: Bool = false @Published var isFeedView: Bool init(url: URL, isFeedView: Bool = true) { player = AVPlayer(url: url) lastKnownTime = nil self.isFeedView = isFeedView if isFeedView { registerForPlaybackEndNotification() } } private func registerForPlaybackEndNotification() { NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem, queue: nil) { [weak self] _ in self?.videoDidFinish() } } private func videoDidFinish() { if isFeedView, let player = player { player.seek(to: .zero) player.play() } } }
额外说明
- 把
contentShape移到ZStack后,整个视频的占位区域都会成为点击响应区 exclusiveGesture的使用确保双击手势优先被识别,解决了单双击互相干扰的问题- 强制
VideoPlayer填充父容器,避免出现视觉上的视频区域和实际点击区域不匹配的情况
内容的提问来源于stack exchange,提问作者user16961399
相关产品推荐
相关产品推荐

