28

在下面的代码中,观察到的对象会更新,但观察它的 View 不会。知道为什么吗?

该代码在屏幕上显示 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))
}
4

4 回答 4

30

@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生成常量视图的组按设计(记录在案!)。

!!这就是原因 - 您需要 dynamic ForEach,但需要更改该模型。

迭代 3 - 最终:

动态ForEach构造函数要求迭代的数据元素是可识别的,因此我们需要 struct 作为模型和更新的视图模型。

这是最终解决方案和演示(使用 Xcode 11.4 / iOS 13.4 测试)

演示2

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
}
于 2020-03-31T18:20:09.490 回答
9

我遇到了同样的问题。对我来说,用@StateObject替换@ObservedObject是可行的。

于 2021-04-01T08:56:58.157 回答
4

使用你的洞察力,@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))
}
于 2020-03-31T23:10:38.633 回答
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))
    
}

于 2021-04-01T10:47:12.387 回答