如何在每个选择更改的 NavigationSplitView 详细信息上运行“task”或“onAppear”?

gre*_*rey 6 swiftui swiftui-navigationlink swiftui-navigationsplitview

我试图NavigationSplitView与其中DetailView包含taskor 的a 一起使用onAppear,但它似乎只运行一次。

enum MenuItem: String, Hashable, Identifiable, CaseIterable {
    case menu1
    case menu2
    case menu3
    case menu4
    case menu5
    
    var id: String { rawValue }
}

struct ContentView: View {
    @State var selection: MenuItem?
    
    var body: some View {
        NavigationSplitView {
            List(MenuItem.allCases, selection: $selection) { item in
                NavigationLink(value: item) {
                    Text(item.rawValue)
                }
            }
        } detail: {
            if let selection {
                DetailView(menuItem: selection)
            } else {
                Text("Default")
            }
        }
    }
}

struct DetailView: View {
    let menuItem: MenuItem
    @State var name = "Name"
    
    var body: some View {
        VStack {
            Text(menuItem.id)
            Text(name)
        }
        .task {
            // This should be an async setup code
            // but for the sake of simplicity
            // I just made it like this
            name = menuItem.id
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

初始应用程序负载 在此输入图像描述

初始菜单选择 在此输入图像描述

第二至第五菜单选择 在此输入图像描述

我知道我可以使用它onChange(of: selection)作为解决方法,然后将我的设置代码放在那里。但是还有其他方法可以在我的内部制作taskonAppear工作DetailView吗?

mar*_*rux 8

将视图id基于选择并不是一个好主意。body每次选择更改时,它都会强制进行整个重建,这将导致随着视图层次结构的增长而导致性能下降。

\n

相反,您可以使用task(id:priority:_:)的替代形式在值更改时启动任务selection,如下所示:

\n
struct ContentView: View {\n    @State var selection: MenuItem?\n    \n    var body: some View {\n        NavigationSplitView {\n           \xe2\x80\xa6\n        } detail: {\n           \xe2\x80\xa6\n        }\n        .task(id: selection, priority: .userInitiated) { sel in\n            print("selection changed:", sel)\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n


Asp*_*eri 5

这是 SwiftUI 优化,它仅重新创建依赖部分。

一个可能的解决方案是使整个主体依赖于菜单项,因此它将完全重新创建并再次调用任务,例如

struct DetailView: View {
    let menuItem: MenuItem
    @State var name = "Name"
    
    var body: some View {
        VStack {
            Text(menuItem.id)
            Text(name)
        }
        .task {
            // This should be an async setup code
            // but for the sake of simplicity
            // I just made it like this
            name = menuItem.id
        }
        .id(menuItem.id) // << here !!
    }
}
Run Code Online (Sandbox Code Playgroud)