SwiftUI父视图的onTapGesture拦截了来自嵌入的AVPlayerViewController的点击事件
我在一个包含父级 .onTapGesture 的SwiftUI视图层次结构中,通过 UIViewControllerRepresentable 嵌入一个 AVPlayerViewController。
在某些较旧的iOS版本中,点击视频播放器也会被父级SwiftUI手势识别。在下面的示例中,当我点击视频播放器时,会打印出“hello”。AVPlayerViewController 控件(播放/暂停、进度条等)会正确地出现和隐藏,但控件本身有时对点击没有响应。
看起来父级SwiftUI手势识别器正在与嵌入的UIKit控制器所接收的触摸发生竞争或拦截。 在iOS 26上测试,效果如预期。请问谁能帮我解决在点击视频播放器时不打印“hello”,并让控件能够正确响应点击的问题吗?
先行感谢!
struct VideoPlayer: UIViewControllerRepresentable {
typealias UIViewControllerType = AVPlayerViewController
private let player: AVPlayer?
init(player: AVPlayer?) {
self.player = player
player?.allowsExternalPlayback = false
}
func makeUIViewController(context: Context) -> AVPlayerViewController {
let controller = AVPlayerViewController()
controller.player = player
controller.showsPlaybackControls = true
return controller
}
func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {}
}
struct VideoPlayerView: View {
@State
private var player: AVPlayer?
let data: Data
let design: Design
init(player: AVPlayer? = nil, data: Data, design: Design) {
self.player = URL(string: data.videoUrl).map(AVPlayer.init(url:))
self.data = data
self.design = design
}
var body: some View {
inlinePlayer
.frame(height: design.playerFixedHeight)
.clipShape(RoundedRectangle(cornerRadius: 5))
.contentShape(RoundedRectangle(cornerRadius: 5))
}
private var inlinePlayer: some View {
VideoPlayer(player: player)
}
}
extension VideoPlayerView {
struct Data {
let videoUrl: String
}
}
extension VideoPlayerView {
struct Design {
let playerFixedHeight: CGFloat = 300
}
}
struct ContentView: View {
var body: some View {
VStack {
Text("Hello, world!")
.background(Color.blue.frame(height: 100))
VideoPlayerView(data: .init(videoUrl: "https://www.pexels.com/download/video/5340598/"), design: .init())
}
.padding()
.onTapGesture {
print("hello")
}
}
}
解决方案
在你提到的问题中:
Tested on iOS 26 works as it should.
当我在iPhone 17模拟器上使用iOS 26.5测试时,点击手势只有在点击带蓝色背景的文本时才会触发。所以如果这是你希望它工作的方式,那么可能的变通方案有:
1.将点击手势附加到 Text 上,而不是附加到 VStack 上
手势应在蓝色背景之后应用,这样也会作用于背景:
VStack {
Text("Hello, world!")
.background(Color.blue.frame(height: 100))
.onTapGesture { // 👈 moved to here
print("hello")
}
VideoPlayerView(data: .init(videoUrl: "https://www.pexels.com/download/video/5340598/"), design: .init())
}
.padding()
2.给 VStack 添加背景,并将点击手势附着在此上
采用这种方法,你需要把 .allowsHitTesting(false) 应用于 Text(蓝色背景之后),以允许手势透传。
也可以把 Color.clear 作为 VStack 背后的背景——如果这样做,请应用一个内容形状使其对点击有响应:
VStack {
Text("Hello, world!")
.background(Color.blue.frame(height: 100))
.allowsHitTesting(false) // 👈 added
VideoPlayerView(data: .init(videoUrl: "https://www.pexels.com/download/video/5340598/"), design: .init())
}
.padding()
.background { // 👈 added
Color.clear
.contentShape(.rect)
.onTapGesture { // 👈 moved to here
print("hello")
}
}
以这种方式应用点击手势时,蓝色矩形左侧和右侧的空白区域也会对点击做出响应。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。