List view not hiding/unhiding elements based on a state variable

huangapple go评论54阅读模式
英文:

List view not hiding/unhiding elements based on a state variable

问题

我明白你只需要代码部分的中文翻译。以下是你提供的代码的翻译:

// 我有一个 NavigationLinks 的列表。每个 NavigationLink 都被一个 "isVisible" 布尔值检查包装。
// 分开,一个配置页面有 Toggle,用于设置项目的可见性。

// 问题是,当我从配置页面返回时,主视图不会根据所有项目上的新可见性布尔值更新自身。即 "if (hv.isVisible)" 不会被重新评估。

// 我知道这些布尔值被持久化了。当我重新启动应用程序时,列表中显示了正确的项目

@ObservedObject fileprivate var viewModel = HealthValuesViewModel.instance
// 在这里,values 被定义为
// @Published var values: [HealthValue] = []
// 然后在 init() 中填充
// HealthValue 是一个 ObservableObject 类,拥有属性
// @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 是主视图 - 具有设置按钮和 3 个对象的列表。
// DummySettingsView 是子视图,用于在列表中切换项目的显示/隐藏状态。
// DummySettingsCellView 是设置视图列表中的元素,具有切换按钮。

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)
                    }
                }
            }
        }
    }
}

希望这对你有所帮助。

英文:

I've got a List of NavigationLinks. Each NavigationLink is wrapped by a "isVisible" boolean check.
Separately, a config page has Toggle's that set the visibility on or off.

The problem is when I come back from the config page, the main view doesnt update itself based on the new visibility boolean on all the items. i.e. the "if (hv.isVisible)" isn't reevaluated.

I know the booleans are being persisted. When I restart the app, the correct items in the list are shown

@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 )
}
}
}

I've been playing with all sorts of combinations of @ObervedObject, @Published etc.
Also tried making sure I have an id in the List.

Any help or suggestions greatly appreciated.

UPDATE:
As suggested, I've created a reproducible example. Hope this makes things clearer

// 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)
}
}
}
}
}
}

I should probably have mentioned that this is a watch app - but I guess it shouldnt make a difference.

答案1

得分: 1

尝试这种方法,使用struct替代嵌套的ObservableObject来定义DummyValueObject,并使用@Binding var value: DummyValueObject将其传递给DummySettingCellView

此外,请注意使用List(model.values.filter{$0.isVisible})而不是使用if (v.isVisible) {...},以确保视图能够正确刷新。

struct ContentView: View {
    var body: some View {
        NavigationStack {
            DummyView()
        }
    }
}

struct DummyView: View {
    @StateObject var model = DummyViewModel()
    
    var body: some View {
        VStack {
            NavigationLink(destination: DummySettingsView(model: model)) {
                Image(systemName: "wrench")
            }
            Spacer()

            List(model.values.filter{$0.isVisible}) { v in
                HStack {
                    Image(systemName: v.symbolName)
                    Text(v.type.rawValue)
                }
            }
        }
    }
}

struct DummySettingsView: View {
    @ObservedObject var model: DummyViewModel
    
    var body: some View {
        VStack {
            List($model.values) { $v in
                DummySettingCellView(value: $v)
            }
        }
    }
}

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 {
    let id = UUID()

    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

    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"
}

此外,您可以使用嵌套的ObservableObjects,但这可能会引入其他问题。在这种情况下,仍然应在DummySettingCellView中使用@Binding var value: DummyValueObject来解决当前问题。不过,请注意,不要使用@ObservedObject var model = DummyViewModel.instance,这是不正确的。@StateObject var model = DummyViewModel()DummyView 中应该是您应用程序的唯一数据源,将此模型传递给需要它的视图。请查看Managing model data in your app链接,以获取有关如何管理应用程序数据的官方示例。

英文:

Try this approach, using a struct for the DummyValueObject instead of a nested ObservableObject, and a @Binding var value: DummyValueObject to pass it into DummySettingCellView

Also note, the important use of List(model.values.filter{$0.isVisible}) instead of using
if (v.isVisible) {...}, to allow the view to refresh properly.

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

You can of course use (not recommended), your nested ObservableObjects.
In that case, follow this approach, still using @Binding var value: DummyValueObject in DummySettingCellView, it will fix your current problem, but may create others
in the other parts of your code, if not now, then later on.

Note, you should not use @ObservedObject var model = DummyViewModel.instance thinking
you can use it as a singleton in different views, this is not correct.

The @StateObject var model = DummyViewModel() in DummyView should be the
only source of truth for your app. Pass this model around to the views that need it.

Have a look at this link, it gives you some good official examples of how to manage data in your app
Managing model data in your app

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)
}
}
}
}
}

huangapple
  • 本文由 发表于 2023年3月31日 21:41:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/75899225.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定