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

时间:2020-02-26 10:50:48

标签: swift swiftui swiftui-state

我有一个如此简单的选中标记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关闭/回调保存了更改,并且刷新了。

但是在外部刷新(如推送通知)刷新核心数据中的基础模型之后,此视图的刷新未正确更改@State属性isChecked。

在init()中,我正在获取isChecked的新值,并使用State(initialValue:)重新初始化@State。但是在体内,我从旧的@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")

        }

    }
}

1 个答案:

答案 0 :(得分:1)

isChecked作为@State变量 是您的真理之源。这意味着,当某些基础数据模型(例如CoreData)更改时,没关系。您的视图仅查看@State的本地isChecked版本。

但是看看你的看法。对我来说,它不应该拥有自己的状态。为什么?因为此视图作为复选标记视图没有语义含义。它的父级似乎拥有该状态(因此为什么会有onCheck回调)。相反,这应该使用带有{em> no 回调的@Binding

struct RoundedCheckmark: View {
    @Binding var isChecked: Bool

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

现在,您的父级拥有状态,您可以推断其语义:

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