SwiftUI 观察变化

标签 swift swiftui combine observableobject

概述:

  • 我有一个名为 Player 的类和一个名为 Song 的类。
  • 播放器包含一首歌曲
  • 一个 View 正在显示歌曲标题

目标:

当我更改 player.song.title 时, View 需要更新。

问题:

当歌曲的属性改变时,它不会自动更新 View 。只有分配新歌曲时,更改才会反射(reflect)出来。

我的尝试:

我已经进行了 2 次尝试(下面的代码),都按预期工作。

问题:

  • 有更好的方法吗? (这似乎是一个常见的问题,人们会遇到。) 我的尝试是否合理,尝试 2 是否更好?
  • 或者我的设计存在根本性缺陷? (我希望播放器中有这首歌,因为它代表当前歌曲)。

原始代码( View 不会更新):

型号

import Foundation
import Combine

class Player : ObservableObject {

    @Published var duration = 0
    @Published var song     : Song

    init(song: Song) {
        self.song = song
    }
}

class Song : ObservableObject {

    @Published var id     : Int
    @Published var title  : String
    @Published var artist : String

    init(id: Int,
         title: String,
         artist: String) {

        self.id     = id
        self.title  = title
        self.artist = artist
    }
}

查看

import SwiftUI

struct ContentView: View {

    @ObservedObject var player : Player

    var body: some View {

        VStack {
            Text(String(player.duration))
            Text(player.song.title)
            Text(player.song.artist)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {

        let song = Song(id: 1, title: "title1", artist: "artist1")
        let player = Player(song: song)

        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            player.song.title = "title2"
        }

        return ContentView(player: player)
    }
}

尝试 1 - 使用 CombineLatest

问题:随着歌曲中属性数量的增加,它的可扩展性不是很好。

class Player : ObservableObject {

    @Published var duration = 0
    @Published var song     : Song
    private var songChangeCanceller : AnyCancellable?

    init(song: Song) {
        self.song = song

        songChangeCanceller = song.$title.combineLatest(song.$artist, song.$id).sink { _, _, _ in
            self.objectWillChange.send()
        }
    }
}

尝试2:使用objectWillChange.sink

class Player : ObservableObject {

    @Published var duration = 0
    @Published var song     : Song

    private var songChangeCanceller : AnyCancellable?
    private var songAttributesChangeCanceller : AnyCancellable?

    init(song: Song) {
        self.song = song

        songChangeCanceller = $song.sink { newSong in

            self.songAttributesChangeCanceller = newSong.objectWillChange.sink { _ in
                self.objectWillChange.send()
            }
        }
    }
}

最佳答案

只需按如下方式简化模型,更新即可。使用 Xcode 11.4/iOS 13.4 测试

class Player : ObservableObject {

    @Published var duration = 0
    @Published var song: Song // published as soon as song.title changed

    init(song: Song) {
        self.song = song
    }
}

struct Song : Identifiable { // value type
    var id     : Int
    var title  : String
    var artist : String
}

关于SwiftUI 观察变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61249506/

相关文章:

Swift:如果定时器的引用计数器变为零,那么它会发生什么情况?

json - 如何获取json字段?

ios - 快速错误: "Type ' ( )' cannot conform to ' View'; only struct/enum/class types can conform to protocols"when calling function to write text

Swift Combine .debounce 内存泄漏?

swift - 使用 combine's Publisher 在 SwiftUI Image 中从远程 URL 异步加载图像

ios - Getter 与计算属性。与其他方法相比,使用其中一种方法有什么理由呢?

ios - 如何保存结果集并在 TableView 中显示所有数据

ios - SwiftUI 显示/隐藏 NavigationBar 的标题问题

ios - 使用 SwiftUI 的 Braintree Drop-In

swift - 如何在SwiftUI中使用onReceive从ObservedObject获取数据?