在init()构造函数中初始化的带有@State的SwiftUI视图

3
我是您的助手,以下是翻译结果,仅保留 HTML 标签:

我有一个非常简单的 SwiftUI 查看器。它显示在列表中,可以通过点击进行切换,或者在从数据源(例如核心数据)刷新时进行切换。

我的第一次实现是:

struct RoundedCheckmark: View {

    @State var isChecked : Bool
    let onCheck: (Bool) -> Void

    func toggle() { isChecked = !isChecked; onCheck(isChecked) }

    init(isChecked: Bool, onCheck: @escaping (Bool) -> Void = { _ in }) {
        self._isChecked = State(initialValue: isChecked)
        self.onCheck = onCheck
    }

    var body: some View {

        Button(action: toggle) {

            Image(isChecked ? "CheckedCheckmark" : "UncheckedCheckmark")
        }
    }
}

看起来它可以工作,我可以切换它并且它可以正确加载。在切换时,我通过onCheck闭包/回调保存更改,并且它可以正常刷新。

但是,在像推送通知这样的外部刷新(例如Core Data底层模型的刷新)之后,此视图的刷新不会正确更改@State属性isChecked。

在init()中,我获取了isChecked的新值,并使用State(initialValue:)重新初始化了@State。但是在body中,我从旧的@State属性中获取了isChecked的旧值,因此视图具有错误的图像。

这里有一个解决方法,即仅依赖于let isChecked属性和onCheck回调。但是现在我不能在我的View内部拥有状态,我只能依赖于外部数据源的更改。也许这更合适,因为这样我就有了单一的真相来源,而无需本地@State存储。

struct RoundedCheckmark: View {

    let isChecked: Bool
    let onCheck: (Bool) -> Void

    func toggle() { onCheck(isChecked) }

    init(isChecked: Bool, onCheck: @escaping (Bool) -> Void = { _ in }) {

        self.isChecked = isChecked
        self.onCheck = onCheck
    }

    var body: some View {

        Button(action: toggle) {

            Image(isChecked ? "CheckedCheckmark" : "UncheckedCheckmark")

        }

    }
}

看起来你自己回答了自己的问题... - undefined
是的,但我不明白为什么第一个解决方案不起作用。是因为每个 View 实例都会创建一个新的 @State,但 View 却使用旧的 @State 吗?如果是这样的话,我该如何改变这个状态(以及是否应该尝试改变它)?你是说这样的话,我会有两个真实数据源吗? - undefined
1个回答

1

isChecked作为一个@State变量,是您的真实数据来源。这意味着当一些底层数据模型(如CoreData)发生更改时,它并不重要。您的视图只查看@State版本的本地isChecked

但是看看您的视图。对我来说,它不应该拥有自己的状态。为什么?因为作为勾选视图,这个视图没有语义含义。它的父级似乎拥有状态(因此有一个onCheck回调)。相反,应该使用@Binding没有回调:

struct RoundedCheckmark: View {
    @Binding var isChecked: Bool

    var body: some View {
        Button(action: { isChecked.toggle() }) {
            Image(isChecked ? "CheckedCheckmark" : "UncheckedCheckmark")
        }
    }

现在您的parent拥有状态,您可以推断其语义含义:
struct CheckmarkOwner: View {
    @State var showFavoritesOnly = false

    var body: some View {
        // content

        RoundedCheckmark(isChecked: $showFavoritesOnly)

        // and now something else will get notified when `showFavoritesOnly` gets toggled
        if showFavoritesOnly {
             // toggleable content
        }

        // more content
    }
}

我已经测试过了,在其他地方它是有效的,例如如果我将一些外部数据传递给init(),然后使用它们来初始化@State与State(initialValue: )。然后,如果这些数据在外部源中刷新,那么init()会再次被调用,并且State(initialValue: )会被重新初始化,通常情况下都能正常工作。还有一种情况,当这些外部数据是Core Data管理对象时,重要的是要知道这个对象就像一个草稿本,有时我们可以更改它并认为在刷新后它没有正确更新,但其状态并没有通过ManagedObjectContext.rollBack回滚。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接