我的应用程序中有一个 NavigationSplitView,我的详细视图中有一个在 init 中创建的 @State 变量。
当我从侧边栏中选择某些内容并呈现详细视图时,起初一切看起来都不错。但是,当我在侧边栏上选择不同的项目时,@state 变量的内容不会重新创建。
使用调试器,我可以看到每次在侧边栏中选择一个新项目时都会调用详细视图的 init,并且可以看到创建了 @State 变量。但当它实际渲染时,@State 变量仍然包含先前选择的值。
我已将这个问题简化为一个测试用例,我将粘贴在下面。详细信息视图中的顶部文本是从侧边栏传入的变量,第二行文本由@State变量生成。预期的行为是,如果我选择“一”,详细信息视图将显示“一”和“名称是一”。如果我选择“二”,详细视图将显示“二”和“名称是二”。
相反,如果我首先选择“one”,它会正确显示。但是当我选择“二”时,它显示“二”和“名称是一”。
请注意,如果我选择“二”作为启动应用程序后执行的第一件事,它会正确显示“二”和“名称是二”,但是当我接下来单击“一”时,它将显示“一”和“名字是二”。所以状态变量被设置一次,然后就不会再改变,
这是示例代码和屏幕截图:
import SwiftUI
struct Item: Hashable, Identifiable {
let id = UUID()
let name: String
}
struct ContentView: View {
@State private var selectedItem: Item.ID? = nil
private let items = [Item(name: "one"), Item(name: "two"), Item(name: "three")]
func itemForID(_ id: UUID?) -> Item? {
guard let itemID = id else { return nil }
return items.first(where: { item in
item.id == itemID
})
}
var body: some View {
NavigationSplitView{
List(selection: $selectedItem) {
ForEach(items) { item in
Text(item.name)
.tag(item.id)
}
}
} detail: {
if let name = itemForID(selectedItem)?.name {
DetailView(name: name)
} else {
Text("Select an item")
}
}
}
}
struct DetailView: View {
@State var detailItem: DetailItem
var name: String
init(name: String) {
self.name = name
_detailItem = State(wrappedValue: DetailItem(name: name))
}
var body: some View {
VStack {
Text(name)
Text(detailItem.computedText)
}
}
}
struct DetailItem {
let name: String
var computedText: String {
return "The name is \(name)"
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Run Code Online (Sandbox Code Playgroud)
这与 NavigationSplitView 无关,与您如何初始化 @State 属性有关。
\n根据苹果关于 @State 的文档(https://developer.apple.com/documentation/swiftui/state):
\n\n\n不要\xe2\x80\x99t 在视图层次结构中实例化视图的位置初始化视图的状态属性,因为这可能与 SwiftUI 提供的存储管理冲突。
\n
以及 init(wrappedValue:) 的文档(https://developer.apple.com/documentation/swiftui/state/wrappedvalue):
\n\n\n不要直接调用此初始化程序。相反,使用 State 属性声明一个属性,并提供一个初始值:
\n
@State private var isPlaying: Bool = false\n
Run Code Online (Sandbox Code Playgroud)\n根据我的理解,如果您强制在视图 init 中初始化状态,它将在视图的整个生命周期中持续存在,并且它的后续更改不会对视图产生任何影响。
\nApple文档中推荐的方法是在父视图中创建结构并将其传递给子视图,如果需要更改子视图中的结构,请使用@Binding以允许读写访问。
\n如果您想忽略文档并强制它工作,您可以为 DetailView 提供一个 id,强制它在项目 id 更改时刷新视图:
\nvar body: some View {\n NavigationSplitView{\n List(selection: $selectedItem) {\n ForEach(items) { item in\n Text(item.name)\n .tag(item.id)\n }\n }\n } detail: {\n if let name = itemForID(selectedItem)?.name {\n DetailView(name: name).id(selectedItem)\n } else {\n Text("Select an item")\n }\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
归档时间: |
|
查看次数: |
816 次 |
最近记录: |