如何避免重新进入NavigationLink时子视图被重复创建、丢失状态?
问题根因
- 你给
SubView实现的Equatable协议逻辑无效:SubView的id是@State属性,属于视图内部的持久化存储,在视图实例初始化阶段还未完成绑定,初始化时读取的id仅为临时值,新旧SubView实例的比对逻辑完全失效,SwiftUI会判定每次生成的SubView都是新实例,直接销毁旧视图重建,导致@State状态重置。 TipsPLView的body每次刷新时,都会执行SubView()构造新的实例,视图标识没有被持久化持有,每次进入导航页触发body刷新就会生成新的SubView。
解决方案
方案1:状态向上提升到父视图持有
把SubView需要留存的状态放到父视图TipsPLView中存储,作为参数传入SubView,只要TipsPLView不被重建,状态就不会丢失。
修改后代码:
// TipsPLView修改部分 struct TipsPLView : View,Identifiable,Equatable { var id = UUID() // 父视图持有SubView的状态 @State private var subNum = 0 static func == (lhs: TipsPLView, rhs: TipsPLView) -> Bool { return lhs.id == rhs.id } init(){ print("init TipsPLView") } var body: some View { VStack{ List{ Section(header:Text("TipsPLView").font(.system(size:20)).foregroundColor(.yellow)){ // 把状态作为绑定参数传入SubView SubView(num: $subNum).equatable() } }.listStyle(InsetGroupedListStyle())} } } // SubView修改部分 struct SubView:View,Equatable { // 改为绑定属性,状态由父视图提供 @Binding var num: Int // 调整比对逻辑,只要状态值相同就判定视图无需重建 static func == (lhs: SubView, rhs: SubView) -> Bool { return lhs.num == rhs.num } init(num: Binding<Int>){ _num = num print("Init SubView") } var body: some View { VStack{ Button(action:{num += 1}){ Text("\(num)") } } } }
方案2:用独立ViewModel存储状态
如果SubView的状态逻辑复杂,可新建ObservableObject类型的ViewModel存储状态,在父视图中用@StateObject持有,生命周期和父视图绑定,不受视图结构刷新影响。
修改后代码:
// 新增SubView的独立状态容器 class SubViewModel: ObservableObject { @Published var num = 0 func incNum() { num += 1 } } // TipsPLView修改部分 struct TipsPLView : View,Identifiable,Equatable { var id = UUID() // 用@StateObject持有VM,生命周期和当前TipsPLView绑定 @StateObject private var subVM = SubViewModel() static func == (lhs: TipsPLView, rhs: TipsPLView) -> Bool { return lhs.id == rhs.id } init(){ print("init TipsPLView") } var body: some View { VStack{ List{ Section(header:Text("TipsPLView").font(.system(size:20)).foregroundColor(.yellow)){ SubView(vm: subVM).equatable() } }.listStyle(InsetGroupedListStyle())} } } // SubView修改部分 struct SubView:View,Equatable { @ObservedObject var vm: SubViewModel static func == (lhs: SubView, rhs: SubView) -> Bool { return lhs.vm.num == rhs.vm.num } init(vm: SubViewModel){ self.vm = vm print("Init SubView") } var body: some View { VStack{ Button(action:{vm.incNum()}){ Text("\(vm.num)") } } } }
如果需要实现退出导航页再进入状态依然留存的效果,将状态存储到更上层视图(如MainPagePLView)或者使用AppStorage、全局状态容器即可。
内容的提问来源于stack exchange,提问作者jeep
相关产品推荐
相关产品推荐

