我有一个导航链接列表。每个NavigationLink由一个";isVisible";布尔值检查。另外,一个配置页面有Toggle,可以设置可见性的打开或关闭
问题是,当我从配置页面返回时,主视图并没有根据所有项目的新可见性布尔值进行更新。即";如果(hv.isVisible)";没有重新评估。
我知道布尔派一直在坚持。当我重新启动应用程序时,列表中的正确项目显示为
@ObservedObject fileprivate var viewModel = HealthValuesViewModel.instance
// in here values is defined as
// @Published var values: [HealthValue] = []
// which is then populated in init()
// The HealthValue is an ObservableObject class and has property
// @Published var isVisible: Bool
List(viewModel.values, id:.type.rawValue) { hv in
if (hv.isVisible) {
NavigationLink(destination: hv.view) {
HealthValueCellView(healthValue: hv )
}
}
}
我一直在玩各种组合的@ObervedObject、@Published等。还试着确保我在列表中有一个id。
非常感谢任何帮助或建议。
更新:按照建议,我创建了一个可复制的示例。希望这能让事情变得更清楚
// DummyView is the main view - has a settings button and a list of 3 objects.
// DummySettingsView is the sub-view where the items in the list are toggled on/off
// DummySettingsCellView are the elements in the settings view list that have the toggle
import SwiftUI
enum ValueType: String {
case type1 = "T1"
case type2 = "T2"
case type3 = "T3"
}
class DummyValueObject : Identifiable, ObservableObject {
let type: ValueType
let symbolName: String
@Published var isVisible: Bool = true {
didSet {
print("ValueObject type=(type.rawValue), isVisible = (isVisible)")
}
}
init(type: ValueType, symbolName: String) {
self.type = type
self.symbolName = symbolName
}
}
class DummyViewModel : ObservableObject {
static let instance = DummyViewModel()
@Published var values: [DummyValueObject] = [
DummyValueObject(type: .type1, symbolName: "heart.fill"),
DummyValueObject(type: .type2, symbolName: "wind"),
DummyValueObject(type: .type3, symbolName: "scalemass.fill")
]
}
struct DummySettingCellView: View {
@ObservedObject var value: DummyValueObject
var body: some View {
HStack {
Image(systemName: value.symbolName)
Text(value.type.rawValue)
Toggle("", isOn: $value.isVisible)
}
}
}
struct DummySettingsView: View {
@ObservedObject var model = DummyViewModel.instance
var body: some View {
VStack {
List(model.values) { v in
DummySettingCellView(value: v)
}
}
}
}
struct DummyView: View {
@ObservedObject var model = DummyViewModel.instance
var body: some View {
VStack {
NavigationLink(destination: DummySettingsView()) {
Image(systemName: "wrench")
}
Spacer()
List(model.values, id:.type.rawValue) { v in
if (v.isVisible) {
HStack {
Image(systemName: v.symbolName)
Text(v.type.rawValue)
}
}
}
}
}
}
我可能应该提到这是一款手表应用程序,但我想它应该没有什么区别。
尝试这种方法,对DummyValueObject
使用struct
而不是嵌套的ObservableObject
,并使用@Binding var value: DummyValueObject
将其传递到DummySettingCellView
另请注意,重要使用List(model.values.filter{$0.isVisible})
而不是使用if (v.isVisible) {...}
,以允许视图正确刷新。
struct ContentView: View {
var body: some View {
NavigationStack { // <-- here
DummyView()
}
}
}
struct DummyView: View {
@StateObject var model = DummyViewModel() // <-- here
var body: some View {
VStack {
NavigationLink(destination: DummySettingsView(model: model)) { // <-- here
Image(systemName: "wrench")
}
Spacer()
List(model.values.filter{$0.isVisible}) { v in // <-- here important
HStack {
Image(systemName: v.symbolName)
Text(v.type.rawValue)
}
}
}
}
}
struct DummySettingsView: View {
@ObservedObject var model: DummyViewModel // <-- here
var body: some View {
VStack {
List($model.values) { $v in // <-- here
DummySettingCellView(value: $v) // <-- here
}
}
}
}
class DummyViewModel : ObservableObject {
@Published var values: [DummyValueObject] = [
DummyValueObject(type: .type1, symbolName: "heart.fill"),
DummyValueObject(type: .type2, symbolName: "wind"),
DummyValueObject(type: .type3, symbolName: "scalemass.fill")
]
}
struct DummyValueObject : Identifiable { // <-- here
let id = UUID() // <-- here
let type: ValueType
let symbolName: String
var isVisible: Bool = true {
didSet {
print("---> DummyValueObject type=(type.rawValue), isVisible = (isVisible)")
}
}
init(type: ValueType, symbolName: String) {
self.type = type
self.symbolName = symbolName
}
}
struct DummySettingCellView: View {
@Binding var value: DummyValueObject // <-- here
var body: some View {
HStack {
Image(systemName: value.symbolName)
Text(value.type.rawValue)
Toggle("", isOn: $value.isVisible)
Spacer()
}
}
}
enum ValueType: String {
case type1 = "T1"
case type2 = "T2"
case type3 = "T3"
}
EDIT-1
当然,您可以使用(不推荐)您的嵌套ObservableObjects
。在这种情况下,遵循这种方法,仍然在DummySettingCellView
中使用@Binding var value: DummyValueObject
,它将解决您当前的问题,但可能会创建其他问题在代码的其他部分,如果不是现在,那么稍后。
注意,你不应该使用@ObservedObject var model = DummyViewModel.instance
思维您可以在不同的视图中将其用作单例,这是不正确的。
DummyView
中的@StateObject var model = DummyViewModel()
应为仅您的应用程序的真相来源。将此模型传递给需要它的视图。
看看这个链接,它为你提供了一些关于如何管理应用程序中数据的好的官方示例管理应用中的模型数据
class DummyViewModel : ObservableObject {
//static let instance = DummyViewModel() <--- NOT THIS
@Published var values: [DummyValueObject] = [
DummyValueObject(type: .type1, symbolName: "heart.fill"),
DummyValueObject(type: .type2, symbolName: "wind"),
DummyValueObject(type: .type3, symbolName: "scalemass.fill")
]
}
struct DummySettingCellView: View {
@Binding var value: DummyValueObject // <-- here
var body: some View {
HStack {
Image(systemName: value.symbolName)
Text(value.type.rawValue)
Toggle("", isOn: $value.isVisible)
}
}
}
struct DummySettingsView: View {
@ObservedObject var model: DummyViewModel // <-- here
var body: some View {
VStack {
List($model.values) { $v in // <-- here $
DummySettingCellView(value: $v) // <-- here
}
}
}
}
struct DummyView: View {
@StateObject var model = DummyViewModel() // <-- here
var body: some View {
VStack {
NavigationLink(destination: DummySettingsView(model: model)) { // <-- here
Image(systemName: "wrench")
}
Spacer()
// -- here --
List(model.values.filter{$0.isVisible}, id:.type.rawValue) { v in
HStack {
Image(systemName: v.symbolName)
Text(v.type.rawValue)
}
}
}
}
}