我想在名称的SwiftUI中构建一个简单的List
,当点击时,导航到允许修改这些名称的详细视图。
当我使用@State
数组为List
提供名称,并试图将数组中元素的绑定传递给细节视图时,对细节视图中绑定对象的任何修改都将导致不仅细节视图的重绘,而且包含List
的屏幕外视图也将重绘。
一般来说,问题似乎是不可能改变父视图中的数据,这在post on the Apple Developer Forums中有描述。
有post with a similar title on this subject here at StackOverflow和blog posts试图解决这个问题,等等,但是我还没有找到一个好的通用解决方案或设计模式来处理看起来相当常见的用例。
Apple's WWDC 2022 video "The SwiftUI Cookbook for Navigation"完全避开了数据变化的问题,并且只显示了使用静态数据的应用程序的示例(即它们不修改任何食谱)。
下面是我为演示这个问题而编写的一些代码:
//
// ContentView.swift
//
import SwiftUI
struct Item: Hashable, Identifiable {
let id = UUID()
var text: String
}
struct ContentView: View {
@State var items = [Item(text: "foo bar"), Item(text: "biz boz") ]
var body: some View {
NavigationStack {
let _ = { print("render NavigationStack") }()
List(items) { item in
let _ = { print("render List item") }()
NavigationLink(item.text, value: item.id)
}
.navigationDestination(for: UUID.self) { id in
if let index = items.firstIndex(where: {$0.id == id}) {
let _ = { print("render TextField") }()
VStack {
TextField("Value", text: $items[index].text)
.multilineTextAlignment(.center)
Spacer()
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
这里有一段视频展示了这个问题:
请注意,当在文本字段中键入字符时,光标总是跳到字段的末尾,这是因为修改文本时,TextField
的父视图将重新呈现,因此整个详细视图将重新呈现。
我特别想完成的是将父视图的重绘与细节视图中的数据修改分开。如果父视图在屏幕外根本不重绘,那就太理想了,但我仍然需要父视图中的列表根据细节视图中的修改更新其文本。如果可能的话,我还想向细节视图传递一个绑定。但在这一点上,任何功能性的变通方案都会令人满意。
2条答案
按热度按时间k97glaaz1#
若要在局部视图中修改父视图的状态数据时分开重绘父视图,应使用目标数据的副本进行操作,并在需要时手动应用更改。
例如,你可以创建一个
ItemEditingView
,它有自己的text
状态,并与你的Item
示例绑定,然后我们可以实现一个自定义工具栏按钮(“Back”,“保存”等),允许我们只在按下时应用更改。j2qf4p5b2#
所提出的答案绝对是好的,但是让我使用MVVM设计模式提出一个更完整的答案。
首先,你需要一个模型来描述你的数据,有些语言称之为data-class,但是在Swift中,这通常被描述为一个struct。
然后,在MVVM模式中,您有一个"中间人",负责存储、管理、保存数据、检测变化,并通过无线电调谐到称为ObservableObject的"站点",让视图重新绘制自己。
这是本案例的一个示例:
该类有两项职责:首先,将Item数组的更改发布到世界上,无论谁是该 * view-model * 的示例,这都是使用 * decorator***@Published和意图来完成的,当有人想要更改其值时,意图将其保存在存储"var items"中。
看起来描述这个变量更合适的方法是使用private(set)**visibility,但是我没有理会。
然后,您希望在某个视图中显示此存储的列表...
现在,您不需要NavigationLink(项目. self,项目:item),因为这是一个Struct,当你有几个模型要显示在同一个List中时,你可以使用这里提供的更简单的Struct NavigationLink(destination:label)。
对于目的地,您将提供视图,您要显示的所有必要参数.
在标签中只显示列表中显示的内容。
就像这样:
最后但并非最不重要的是,这是编辑你的模型的一个项目并调用适当的Intent到视图模型中的模拟视图,这可以被定制为比这看起来更好。
希望这能为这里已经提到的答案增加一些价值。