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
相关产品推荐
相关产品推荐

