【问题标题】:SwiftUI View not updating based on @ObservedObjectSwiftUI 视图未基于 @ObservedObject 更新
【发布时间】:2020-07-12 08:09:45
【问题描述】:

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

代码在屏幕上显示 10 个数字 (0..

打印语句显示按钮正在更新数字,但视图没有相应更新。我知道更新数组中的值不会改变数组值本身,所以我使用手动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))
}

【问题讨论】:

  • 在我的情况下这是一个愚蠢的问题,我不得不将正在更新的 observedObject 放在主线程上......突然间一切都开始按预期工作
  • 我在回答中回答了你的问题!

标签: ios arrays swiftui observableobject


【解决方案1】:

观察到的对象没有错,您应该使用@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))
    
}

【讨论】:

  • 谢谢您,swiftPunk,但您的代码解决的问题与我的不同。你的工作,因为它用另一个替换每个消除的数字,导致 ForEach 中的项目数量恒定。正如@Asperi 上面指出的那样,我的失败是因为它需要在屏幕上显示可变数量的项目……但我使用的 ForEach 品种需要固定数量的项目。他在他的解决方案中很好地解释了这一点,而我的只是简化了他的。
  • 没问题,只是需要帮助!
【解决方案2】:

我遇到了同样的问题。 对我来说,将 @ObservedObject 替换为 @StateObject 有效。

【讨论】:

  • 这个解决问题的改动和这里的问题没有关系。
  • 我发现这个问题面临一个类似的问题,即异步更改不更新 ObservedObject,实际上这个更改也解决了我的问题。不知道为什么,但如果有人遇到此线程,请务必尝试一下。
  • 我也解决了我的问题,如果有人有它会感兴趣的原因?
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    @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 的构造函数,它生成 constant 视图组 按设计(记录在案!)。

    !!这就是原因 - 您需要动态的 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
    }
    

    【讨论】:

    • 谢谢你,@asperi!我的问题仍然是为什么围绕Text() 视图的if number.visible 子句没有阻止不可见的数字显示。是不是当ForEach 迭代一个常量集合时,它甚至没有收到numberLine 在其闭包中发生变化的通知?
    • 我在带有 xcode 11.6 的 iOS 13 上遇到了同样的问题,但在 iOS 14 beta 上却没有。我确实使用了范围方法。然后我换了一个可识别的模型,但问题仍然存在。将@published 添加到属性中对我来说是个窍门。 iOS 14 测试版一直运行良好。
    • 预期的错误是什么?当我在 Xcode 12.1 中通过海报运行原始代码时,单击按钮确实会更改输出。所以要么我误解了这个错误,要么 Swift 改变了它处理这种情况的方式?
    • 谢谢!!感谢:1) Anton 花时间发布此代码,2) @Asperi 知道答案并花时间写出来,3) StackOverflow 创建了一个平台,其中两个您可以找到彼此,并且 4) Google 奇迹般地将我相当模糊的查询转换为我需要的 exact StackOverflow 链接。我花了 2 分钟的时间来调试一个我担心需要几个小时的东西。谁知道ForEach 的这种行为!? ??
    猜你喜欢
    • 2020-12-21
    • 2021-02-17
    • 2020-05-02
    • 1970-01-01
    • 1970-01-01
    • 2020-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多