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

SwiftUI导航异常求助:点击跳转编辑项后返回初始视图

SwiftUI三层导航栈异常返回问题排查与修复

问题原因

  • Hashable实现错误:InnerDataItem的==和hash方法依赖可变的innerInfo属性,当在编辑页修改innerInfo时,对象的哈希值和相等性会发生变化。SwiftUI的NavigationLink(value:)依赖对象的稳定标识管理导航栈,标识突变会导致导航栈无法正确识别当前页面,触发异常返回。
  • 导航目的地嵌套定义:DataList中嵌套定义navigationDestination,虽不是直接诱因,但不符合SwiftUI导航最佳实践,可能引发导航栈状态管理混乱。

修复方案

  1. 修正Hashable实现:改用不可变的id属性作为InnerDataItem的相等性和哈希判断依据,保证对象标识的稳定性。
  2. 统一导航目的地位置:将所有navigationDestination移至顶层NavigationStack下,让导航栈统一管理所有页面跳转逻辑。

修改后的完整代码

import SwiftUI

@Observable
class InnerDataItem : Hashable, Identifiable {
    let id = UUID() // 改为不可变常量
    var innerInfo = ""
    
    init(innerInfo: String = "") {
        self.innerInfo = innerInfo
    }
    
    static func == (lhs: InnerDataItem, rhs: InnerDataItem) -> Bool {
        lhs.id == rhs.id // 用唯一ID判断相等性
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id) // 用唯一ID生成哈希值
    }
}

@Observable
class DataModel : Identifiable {
    let id: UUID = UUID()
    var info = "My String array"
    var innerData: [InnerDataItem] = []
    
    init(info: String = "My String array", innerData: [InnerDataItem]) {
        self.info = info
        self.innerData = innerData
    }
}

let testData = DataModel(
    info: "Data model object",
    innerData: [
        InnerDataItem(innerInfo: "(1) Inner info One"),
        InnerDataItem(innerInfo: "(1) Inner info Two"),
        InnerDataItem(innerInfo: "(1) Inner info Three"),
    ])

struct DataListView: View {
    var data: DataModel
    
    var body: some View {
        DataList(data: data)
    }
}

struct DataList: View {
    var data: DataModel
    
    var body: some View {
        List {
            ForEach(data.innerData) { item in
                NavigationLink(value: item) {
                    DataLine(dataItem: item)
                }
            }
        }
    }
}

struct DataLine: View {
    var dataItem: InnerDataItem
    
    var body: some View {
        VStack(alignment: .leading) {
            Text(dataItem.innerInfo).font(.largeTitle)
        }
    }
}

struct DataEditItem: View {
    @Bindable var dataItem: InnerDataItem
    
    var body: some View {
        Form {
            Section(header: Text("显示名称")) {
                TextField("", text: $dataItem.innerInfo, prompt: Text("输入内容"))
            }
        }
    }
}

struct ContentView: View {
    @State private var gotoDataList = false
    
    var body: some View {
        NavigationStack {
            List {
                Button("进入数据列表") {
                    gotoDataList.toggle()
                }
            }
            .navigationDestination(isPresented: $gotoDataList) {
                DataListView(data: testData)
            }
            // 将InnerDataItem的导航目的地统一放在顶层
            .navigationDestination(for: InnerDataItem.self) { item in
                DataEditItem(dataItem: item)
            }
        }
    }
}

@main
struct ListNavTestAppApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

#Preview {
    ContentView()
}

额外说明

  • 对于可编辑的Observable对象,禁止用可变属性作为Hashable的判断依据,必须依赖稳定的唯一标识(如UUID)。
  • 所有导航目的地尽量统一放在顶层NavigationStack下,避免嵌套定义导致的导航状态异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 22:07:41