簡體   English   中英

SwiftUI @Published 屬性未更新視圖

[英]SwiftUI @Published property not updating views

在下面的代碼中,點擊 FavoriteButton 時詳細視圖 UI 不會更新。 我們知道綁定已連接到 ObservableObject,因為調用了didSet並打印了foos數組的更新后的 state。 奇怪的是,我發現在FooListNavigationView中添加VStack可以解決此實例中的錯誤,但對於遇到此問題的更復雜的 UI 卻沒有。

我是否缺少有關如何連接的信息? 另一種解決方法是在FavoriteButton中切換State布爾值,但這似乎違背了綁定的觀點。

import SwiftUI

struct Foo: Identifiable {
    var id = UUID()
    var name: String
    var isFavorite: Bool
}

class FooData: ObservableObject {
    @Published var foos = [Foo(name: "Test", isFavorite: true)] {
        didSet {
            print(foos)
        }
    }
}

struct FooList: View {
    @ObservedObject var fooData = FooData()
    
    var body: some View {
        NavigationView {
// Adding a VStack makes the FavoriteButton update correctly, but this doesn't work for more complicated UIs
//            VStack {
                List {
                    ForEach(fooData.foos, id: \.id) { foo in
                        let index = fooData.foos.firstIndex(where: { $0.id == foo.id })!
                        NavigationLink(foo.name, destination: FooDetailView(foo: $fooData.foos[index]))
                    }
                }
//            }
        }
    }
}

struct FooDetailView: View {
    @Binding var foo: Foo
    
    var body: some View {
        FavoriteButton(isFavorite: $foo.isFavorite)
    }
}

struct FavoriteButton: View {
    @Binding var isFavorite: Bool
    
    var body: some View {
        Button(action: {
            isFavorite.toggle()
        }, label: {
            if isFavorite {
                Image(systemName: "heart.fill")
            } else {
                Image(systemName: "heart")
            }
        })
    }
}

更新:

FooData作為EnvironmentObject傳遞給List並在FavoriteButton中聲明EnvironmentObject可以解決此問題。 這必須足以告訴 SwiftUI 該視圖關心對FooData的任何更新,即使沒有代碼直接引用EnvironmentObject 這感覺有點像魔術,對我來說絕對不是最直觀的行為。 希望這可以幫助其他人。

完整的工作代碼:

import SwiftUI

struct Foo: Identifiable {
    var id = UUID()
    var name: String
    var isFavorite: Bool
}

class FooData: ObservableObject {
    @Published var foos = [Foo(name: "Test", isFavorite: true)] {
        didSet {
            print(foos)
        }
    }
}

struct FooList: View {
    @ObservedObject var fooData = FooData()
    
    var body: some View {
        NavigationView {
            List {
                ForEach(fooData.foos, id: \.id) { foo in
                    let index = fooData.foos.firstIndex(where: { $0.id == foo.id })!
                    NavigationLink(foo.name, destination: FooDetailView(foo: $fooData.foos[index]))
                }
            }
        }
        .environmentObject(fooData)
    }
}

struct FooDetailView: View {
    @Binding var foo: Foo
    
    var body: some View {
        FavoriteButton(isFavorite: $foo.isFavorite)
    }
}

struct FavoriteButton: View {
    @EnvironmentObject var fooData: FooData
    @Binding var isFavorite: Bool
    
    var body: some View {
        Button(action: {
            isFavorite.toggle()
        }, label: {
            if isFavorite {
                Image(systemName: "heart.fill")
            } else {
                Image(systemName: "heart")
            }
        })
    }
}

當您有一個 viewModel 發布一個數組時,唯一可以觸發視圖更新的是數組計數。 因此,您需要將數組中的對象class ,將其擴展為ObservableObject ,並將isFavorite @Published var

class Foo: Identifiable, ObservableObject {
  var id = UUID()
  var name: String
  @Published var isFavorite: Bool

  init(name: String, isFavorite: Bool) {
    self.name = name
    self.isFavorite = isFavorite
  }
}

此外,您應該將您的FooData @StateObject ,因為該視圖擁有它。

struct ContentView: View {
  @StateObject var fooData = FooData()

  var body: some View {
    NavigationView {
      List {
        ForEach(fooData.foos) { foo in
          NavigationLink(foo.name, destination: FooDetailView(foo: foo))
        }
      }
    }
  }
}

foo作為@ObservedObject向下傳遞

struct FooDetailView: View {
  @ObservedObject var foo: Foo

  var body: some View {
    Button(action: {
        foo.isFavorite.toggle()
    }, label: {
        if foo.isFavorite {
            Image(systemName: "heart.fill")
        } else {
            Image(systemName: "heart")
        }
    })
  }
}

您可以使用仍然可以訪問ObservableObjectNavigationLink視圖的中間“包裝器”來解決此問題:


struct FooList: View {
    @ObservedObject var fooData = FooData()
    
    var body: some View {
        NavigationView {
                List {
                    ForEach(fooData.foos, id: \.id) { foo in
                        NavigationLink(foo.name, destination: FooWrapper(id: foo.id, fooData: fooData))
                    }
                }
        }
    }
}

struct FooWrapper : View {
    var id : UUID
    @ObservedObject var fooData : FooData
    
    var fooBinding : Binding<Foo> {
        .init { () -> Foo in
            let index = fooData.foos.firstIndex(where: { $0.id == id })!
            return fooData.foos[index]
        } set: { (newValue) in
            let index = fooData.foos.firstIndex(where: { $0.id == id })!
            fooData.foos[index] = newValue
        }
    }
    
    var body: some View {
        FooDetailView(foo: fooBinding)
    }
}

我重用了您的代碼來查找fooindex ——就個人而言,我可能會編寫一個解決方案,而不會在發生崩潰時強制展開,但這是一個與問題不完全相關的問題。

暫無
暫無

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

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