簡體   English   中英

SwiftUI 視圖未基於 @ObservedObject 更新

[英]SwiftUI View not updating based on @ObservedObject

在下面的代碼中,觀察到的對象會更新,但觀察它的 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))
}

使用@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 測試)

演示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
}

備份

我遇到了同樣的問題。 對我來說,用@StateObject替換@ObservedObject是可行的。

使用你的洞察力,@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))
}

觀察到的對象沒有任何問題,您應該在使用觀察到的對象時使用@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))
    
}

問題在於函數,不要忘記在你的 ForEach 函數中添加id: \.self ,並使你的 Model Hashable, Identifiable

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM