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

如何避免重新进入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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 12:15:04