英文:
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)
}
}
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论