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

iOS16 SwiftUI添加.onTapGesture后NavigationLink失效的解决方法

问题修复:SwiftUI NavigationLink添加手势后失效+视频无法暂停的解决方案

问题根源

  1. AVPlayer实例重复创建:你将player定义为计算属性,每次调用该属性(如传给VideoPlayer或BtnFull2)都会生成全新的AVPlayer实例。这导致你暂停的是新创建的空实例,而非正在播放的那个,播放状态完全不受控制。
  2. 手势拦截导航事件:在ButtonStyle中添加onTapGesture会优先拦截点击事件,覆盖NavigationLink的默认导航行为,导致链接无法跳转。

修复步骤与完整代码

1. 封装AVPlayer确保实例唯一

创建ObservableObject包装器,保证整个视图生命周期内只有一个AVPlayer实例:

import SwiftUI
import AVKit

// 包装AVPlayer,避免重复创建
class PlayerWrapper: ObservableObject {
    let player: AVPlayer
    init(url: URL) {
        player = AVPlayer(url: url)
    }
}

2. 重构视图逻辑

  • 用@StateObject持有Player实例,控制播放时机
  • 移除ButtonStyle中的手势逻辑,改用simultaneousGesture同时触发导航与暂停
struct SpecialVideo6: View {
    private let videoURL = Bundle.main.url(forResource: "war", withExtension: "mp4")!
    @Binding var path: NavigationPath
    // 持有唯一的Player实例
    @StateObject private var playerWrapper = PlayerWrapper(url: Bundle.main.url(forResource: "war", withExtension: "mp4")!)

    var body: some View {
        ZStack {
            Color(.black).ignoresSafeArea()
            VideoPlayer(player: playerWrapper.player)
                .scaledToFill()
            
            // 右侧按钮组
            HStack{
                Spacer()
                VStack {
                    NavigationLink("", value: Route.linkSpecials).buttonStyle(BtnVid(vidIcon: "share"))
                    NavigationLink("", value: Route.linkSpecials).buttonStyle(BtnVid(vidIcon: "hearth"))
                    Spacer()
                }
                .frame(maxHeight: 330)
                .padding(.trailing, 16)
                .padding(.top, 50)
            }
            
            // 底部Bet按钮
            VStack {
                Spacer()
                NavigationLink("Bet", value: Route.linkBetBlackOps)
                    .buttonStyle(BtnFull2())
                    // 同时触发导航与暂停,不拦截事件
                    .simultaneousGesture(TapGesture().onEnded {
                        playerWrapper.player.pause()
                    })
            }
            .frame(maxHeight: 330)
        }
        .navigationBarBackButtonHidden()
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button {
                    path.removeLast()
                } label: {
                    HStack(spacing: 0) {
                        Spacer()
                        Image("x")
                    }
                    .padding(.top, 25)
                }
            }
        }
        // 视图出现时启动播放
        .onAppear {
            playerWrapper.player.play()
        }
        // 视图消失时暂停(可选,防止后台持续播放)
        .onDisappear {
            playerWrapper.player.pause()
        }
    }
}

// 简化ButtonStyle,仅处理样式逻辑
struct BtnFull2: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .font(.custom("Bebas Neue", size: 24))
            .foregroundColor(.white)
            .frame(width: 98, height: 50)
            .background(LinearGradient(gradient: Gradient(colors: [Color("lb"), Color("rb")]), startPoint: .top, endPoint: .bottom))
            .cornerRadius(10)
    }
}

// 保留原BtnVid样式实现(假设你已完成)
struct BtnVid: ButtonStyle {
    let vidIcon: String
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .imageScale(.large)
            .foregroundColor(.white)
            .padding()
    }
}

// 假设Route枚举已定义
enum Route: Hashable {
    case linkSpecials
    case linkBetBlackOps
}

关键修复点说明

  • 实例唯一性:通过@StateObject和包装器确保AVPlayer实例在视图生命周期内唯一,暂停操作直接作用于正在播放的视频。
  • 手势不拦截导航:simultaneousGesture让点击事件同时触发导航动作与暂停逻辑,不会覆盖NavigationLink的默认行为。
  • 播放时机可控:将播放逻辑移至onAppear,避免计算属性每次渲染都创建新实例并自动播放。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 18:32:06