在以下代码中,观察到的对象被更新,但是观察到它的视图未更新。知道为什么吗?
该代码在屏幕上显示10个数字(0 .. <10)和一个按钮。每当按下按钮时,它都会随机选择10个数字之一并翻转其可见性(可见→隐藏或反之亦然)。
打印语句显示该按钮正在更新数字,但视图未相应更新。我知道更新数组中的值不会更改数组值本身,因此我使用手动objectWillChange.send()
调用。我本以为应该触发更新,但是屏幕永远不会改变。
有什么主意吗?我对使用NumberLine
作为类或结构,或者根本不使用NumberLine
类型,而只在ContentView
结构中使用数组变量的解决方案感兴趣
以下是代码:
import SwiftUI
struct ContentView: View {
@ObservedObject var numberLine = NumberLine()
var body: some View {
VStack {
HStack {
ForEach(0 ..< numberLine.visible.count) { number in
if self.numberLine.visible[number] {
Text(String(number)).font(.title).padding(5)
}
}
}.padding()
Button(action: {
let index = Int.random(in: 0 ..< self.numberLine.visible.count)
self.numberLine.objectWillChange.send()
self.numberLine.visible[index].toggle()
print("\(index) now \(self.numberLine.visible[index] ? "shown" : "hidden")")
}) {
Text("Change")
}.padding()
}
}
}
class NumberLine: ObservableObject {
var visible: [Bool] = Array(repeatElement(true, count: 10))
}
答案 0 :(得分:17)
有了@ObservedObject
一切都很好...让我们来分析...
迭代1:
无需更改就可以获取代码,仅添加以下行(显示为visible
数组的文本当前状态)
VStack { // << right below this
Text("\(numberLine.visible.reduce(into: "") { $0 += $1 ? "Y" : "N"} )")
并运行,您会看到Text
已更新,因此可观察对象起作用了
迭代2:
删除self.numberLine.objectWillChange.send()
,然后在视图模型中使用默认的@Published
模式
class NumberLinex: ObservableObject {
@Published var visible: [Bool] = Array(repeatElement(true, count: 10))
}
运行,您会看到更新的作用与上面的第一个演示相同。
*但是... ForEach
中的主要数字仍未更新...是的,因为ForEach
中的问题-您将Range
的构造函数与生成常数的视图的组按设计(已记录!)。
!这就是原因-您需要动态ForEach
,但是需要更改该模型。
迭代3-最终版本:
动态ForEach
构造函数要求迭代数据元素是可识别的,因此我们需要将struct作为模型并更新视图模型。
这是最终解决方案和演示(已通过Xcode 11.4 / iOS 13.4测试)
struct ContentView: View {
@ObservedObject var numberLine = NumberLine()
var body: some View {
VStack {
HStack {
ForEach(numberLine.visible, id: \.id) { number in
Group {
if number.visible {
Text(String(number.id)).font(.title).padding(5)
}
}
}
}.padding()
Button("Change") {
let index = Int.random(in: 0 ..< self.numberLine.visible.count)
self.numberLine.visible[index].visible.toggle()
}.padding()
}
}
}
class NumberLine: ObservableObject {
@Published var visible: [NumberItem] = (0..<10).map { NumberItem(id: $0) }
}
struct NumberItem {
let id: Int
var visible = true
}
答案 1 :(得分:2)
根据您的见识@Asperi,问题出在ForEach
上,而不是@ObservableObject
功能上,这是对完成此操作的原始功能的一个小修改:
import SwiftUI
struct ContentView: View {
@ObservedObject var numberLine = NumberLine()
var body: some View {
VStack {
HStack {
ForEach(Array(0..<10).filter {numberLine.visible[$0]}, id: \.self) { number in
Text(String(number)).font(.title).padding(5)
}
}.padding()
Button(action: {
let index = Int.random(in: 0 ..< self.numberLine.visible.count)
self.numberLine.visible[index].toggle()
}) {
Text("Change")
}.padding()
}
}
}
class NumberLine: ObservableObject {
@Published var visible: [Bool] = Array(repeatElement(true, count: 10))
}
答案 2 :(得分:0)
我遇到了同样的问题。 对我来说,用 @StateObject 替换 @ObservedObject 有效。
答案 3 :(得分:0)
观察到的对象没有错,您应该在使用观察到的对象时使用@Published
对象,但我的代码在没有它的情况下也能工作。我还在你的代码中更新了你的逻辑。
import SwiftUI
struct ContentView: View {
@ObservedObject var model = NumberLineModel()
@State private var lastIndex: Int?
var body: some View {
VStack(spacing: 30.0) {
HStack {
ForEach(0..<model.array.count) { number in
if model.array[number] {
Text(String(number)).padding(5)
}
}
}
.font(.title).statusBar(hidden: true)
Group {
if let unwrappedValue: Int = lastIndex { Text("Now the number " + unwrappedValue.description + " is hidden!") }
else { Text("All numbers are visible!") }
}
.foregroundColor(Color.red)
.font(Font.headline)
Button(action: {
if let unwrappedIndex: Int = lastIndex { model.array[unwrappedIndex] = true }
let newIndex: Int = Int.random(in: 0...9)
model.array[newIndex] = false
lastIndex = newIndex
}) { Text("shuffle") }
}
}
}
class NumberLineModel: ObservableObject {
var array: [Bool] = Array(repeatElement(true, count: 10))
}