SwiftUI视图未基于@ObservedObject更新

时间:2020-03-31 17:18:21

标签: ios arrays swiftui observableobject

在以下代码中,观察到的对象被更新,但是观察到它的视图未更新。知道为什么吗?

该代码在屏幕上显示10个数字(0 .. <10)和一个按钮。每当按下按钮时,它都会随机选择10个数字之一并翻转其可见性(可见→隐藏或反之亦然)。

打印语句显示该按钮正在更新数字,但视图未相应更新。我知道更新数组中的值不会更改数组值本身,因此我使用手动objectWillChange.send()调用。我本以为应该触发更新,但是屏幕永远不会改变。

有什么主意吗?我对使用NumberLine作为类或结构,或者根本不使用NumberLine类型,而只在ContentView结构中使用数组变量的解决方案感兴趣

Screenshot

以下是代码:

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

4 个答案:

答案 0 :(得分:17)

有了@ObservedObject一切都很好...让我们来分析...

迭代1:

无需更改就可以获取代码,仅添加以下行(显示为visible数组的文本当前状态)

VStack { // << right below this
    Text("\(numberLine.visible.reduce(into: "") { $0 += $1 ? "Y" : "N"} )")

并运行,您会看到Text已更新,因此可观察对象起作用了

demo

迭代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测试)

demo2

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对象,但我的代码在没有它的情况下也能工作。我还在你的代码中更新了你的逻辑。


enter image description here


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