SwiftUI中@FocusState导致NavigationLink触发两次的问题
问题根源
点击NavigationLink时,系统会自动移除TextField的焦点,导致@FocusState var focussed的值更新,触发ContentView的body重新计算。此时isActive已经为true,body重绘时会重新创建NavigationLink,连带其destination的MyViewTwo再次执行init方法。
解决方案
1. 使用LazyView延迟目标视图初始化
自定义一个LazyView,让目标视图只有在真正被导航到的时候才会初始化,避免父视图重绘时重复创建:
struct LazyView<Content: View>: View { let build: () -> Content init(_ build: @autoclosure @escaping () -> Content) { self.build = build } var body: some View { build() } } // 修改ContentView中的NavigationLink NavigationLink("Tap Me", destination: LazyView(MyViewTwo()), isActive: $isActive)
即使body重绘,LazyView也不会立即创建MyViewTwo,只有导航触发时才会执行init。
2. 手动控制焦点与导航触发
在点击导航按钮时主动取消焦点,避免后续FocusState变化触发的重绘影响:
// 把NavigationLink换成Button + 手动控制isActive Button("Tap Me") { focussed = false // 主动取消焦点 isActive = true } .navigationDestination(isPresented: $isActive) { MyViewTwo() }
用Button触发导航,提前取消焦点,避免FocusState的变化导致body重绘时重复触发导航逻辑。
3. 改用iOS 16+的NavigationStack(推荐)
如果项目支持iOS 16及以上,使用NavigationStack配合navigationDestination修饰符,这种API的视图生命周期更稳定,不会因为父视图重绘而重复初始化目标视图:
struct ContentView: View { @State var text: String = "text" @FocusState var focussed: Bool @State var isActive: Bool = false var body: some View { NavigationStack { // 替换NavigationView为NavigationStack List { TextField("", text: $text) .focused($focussed) .onChange(of: focussed) { _ in } let _ = Self._printChanges() NavigationLink("Tap Me", isActive: $isActive) { EmptyView() // 这里用EmptyView占位 } } .navigationDestination(isPresented: $isActive) { MyViewTwo() // 目标视图在这里声明 } } } }
navigationDestination会独立管理目标视图的创建,父视图body重绘不会影响它,自然不会重复调用init。
内容的提问来源于stack exchange,提问作者Ash
相关产品推荐
相关产品推荐

