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

SwiftUI中NavigationLink点击Alert取消仍非自愿跳转如何解决

问题原因

iOS平台的List存在默认交互行为:只要Cell内部包含NavigationLink,无论是否设置为hidden,点击整个Cell都会自动触发NavigationLink的激活逻辑,这和watchOS的交互逻辑不同。
你代码中点击带Alert的Cell时,两个逻辑并行触发:

  • Button的点击事件弹出Alert
  • Cell默认点击事件把isShowingNext置为true
    当你点击Alert的Cancel按钮后,isShowingAlert变为false,你自定义的Binding判断!isShowingAlert && isShowingNext就返回true,直接触发了跳转。

解决方法

可根据需要支持的系统版本选择对应方案:

方案1:兼容iOS13+,调整结构阻止默认点击

修改RowView的代码,把NavigationLink移到背景层,同时给Button设置PlainButtonStyle避免Cell捕获点击事件,不需要额外自定义Binding:

struct RowView: View {
    let item: String
    @State private var isShowingNext = false
    @State private var isShowingAlert = false

    var body: some View {
        if item != "2" {
            Button(action: {
                isShowingAlert.toggle()
            }) {
                Text("\(item) Show Alert First")
            }
            .buttonStyle(.plain) // 关键配置:阻止Cell捕获点击触发导航
            .background(
                NavigationLink(
                    destination: Text("Hello"),
                    isActive: $isShowingNext,
                    label: { EmptyView() }
                )
            )
            .alert(isPresented: $isShowingAlert) {
                Alert(
                    title: Text("Alert").foregroundColor(.red).font(.title),
                    message: Text("Hello"),
                    primaryButton: .destructive(Text("Navigate"), action: {
                        isShowingNext = true
                    }),
                    secondaryButton: .cancel()
                )
            }
        } else {
            NavigationLink {
                Text("Hello")
            } label: {
                Text("\(item) Show Directly")
            }
        }
    }
}

方案2:iOS16+ 用navigationDestination统一管理路由

直接废弃Cell内的独立NavigationLink,在列表层统一处理跳转逻辑,代码更易维护:

struct ContentView: View {
    let items = ["1", "2", "3", "4"]
    @State private var selectedItem: String?

    var body: some View {
        NavigationStack { // iOS16+替换旧的NavigationView
            List {
                ForEach(items, id: \.self) { item in
                    RowView(item: item, selectedItem: $selectedItem)
                }
            }
            .navigationDestination(item: $selectedItem) { item in
                Text("Hello \(item)")
            }
        }
    }
}

struct RowView: View {
    let item: String
    @Binding var selectedItem: String?
    @State private var isShowingAlert = false

    var body: some View {
        if item != "2" {
            Button("\(item) Show Alert First") {
                isShowingAlert.toggle()
            }
            .buttonStyle(.plain)
            .alert(isPresented: $isShowingAlert) {
                Alert(
                    title: Text("Alert").foregroundColor(.red).font(.title),
                    message: Text("Hello"),
                    primaryButton: .destructive(Text("Navigate"), action: {
                        selectedItem = item
                    }),
                    secondaryButton: .cancel()
                )
            }
        } else {
            Button("\(item) Show Directly") {
                selectedItem = item
            }
            .buttonStyle(.plain)
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 18:54:05