按照 SwiftUI 的设计准则,View 绑定的数据源变化会触发 View 改写。但是在某些情况下咱们需求在数据源没变化的时分自动改写 View。体系没有给 View 供给一个一致的 refresh 或者 reload 办法。于是咱们只能用一些非主流的办法来完成。本文介绍两种办法完成手动触发 View 改写。

自定义ObservedObject

SwiftUI 状况办理 @ObservedObject 的生命周期和 View 保持一致。当 View 被从头求值时,相关的ObservedObject 也会被从头初始化。同理 ObservedObject 变化时也会触发 View 从头求值。因而咱们能够自定义一个 ObservedObject 实例,通过触发 ObservedObject 状况变化促进 View 改写。

代码完成

SwiftUI Tips:如何强制触发View刷新(reload)

定义一个 ObservableObject:

class TriggerViewModel: ObservableObject {
    func updateView() {
        self.objectWillChange.send()
    }
}

在 View 中声明成 @ObservedObject 特点:

struct ContentView: View {
    @ObservedObject var refreshTrigger = TriggerViewModel()
    var body: some View {
        VStack {
            RandomView()
                .frame(width: 150, height: 150)
            Button("改写") {
                updateViewModel()
            }
            .font(.headline)
            .buttonStyle(.borderedProminent)
        }
    }
    private func updateViewModel() {
        refreshTrigger.updateView()
    }
}

有了 refreshTrigger 后,咱们调用内部的 updateView 办法就能够让 View 改写。

Tips:由于每次 View 更新都会对 @ObservedObject 从头求值,但是通常情况下 View 的更新并不需求相关的特点从头初始化。因而 SwiftUI 后边推出了 @StateObject。

Demo 说明:RandomView 每次改写就会变换背景色。

struct RandomView: View {
    let randomH = Double.random(in: 0...1.0)
    let randomS = Double.random(in: 0...1.0)
    let randomL = Double.random(in: 0...1.0)
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 12)
                .foregroundColor(Color(hue: randomH, saturation: randomS, brightness: randomL))
            VStack {
                Text("H: \(randomH)")
                Text("S: \(randomS)")
                Text("L: \(randomL)")
            }
            .fontWeight(.medium)
            .foregroundColor(.white)
        }
    }
}

修正 View 的标识:id

尽管 View 没有供给 reload 办法,但是 View 供给了 id 办法绑定 View 的身份标识。

extension View {
    /// Binds a view's identity to the given proxy value.
    ///
    /// When the proxy value specified by the `id` parameter changes, the
    /// identity of the view — for example, its state — is reset.
    @inlinable public func id<ID>(_ id: ID) -> some View where ID : Hashable
}

因而咱们能够通过修正一个 View 的 id 的值来告诉体系这个 View 已经变化,需求从头改写。

咱们能够把要改写的 View 的 id 绑定到一个时间戳上面。每次要更新的时分对 ViewId 从头赋值就能够了。

沿用上一节的代码,完成是这样的:

struct ContentView: View {
    @State private var refreshViewId = Date().timeIntervalSince1970
    var body: some View {
        VStack {
            RandomView()
                .frame(width: 150, height: 150)
                .id(refreshViewId)
            Button("改写") {
                updateViewModel()
            }
        }
    }
    private func updateViewModel() {
        refreshViewId = Date().timeIntervalSince1970
    }
}