在初始化运行之前布局的SwiftUI视图(显然) [英] SwiftUI View (apparently) laid out before init runs

查看:86
本文介绍了在初始化运行之前布局的SwiftUI视图(显然)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

TL; DR

似乎在init运行之前,下面的ContentView会评估主体的if语句.有比赛条件吗,还是我的心理模型失调了?

荣誉

大声疾呼Asperi,后者提供了解决当今问题的状态初始化程序等效版本.问题.

代码

为什么ContentView显示虚拟为零"?似乎在初始化器设置dummy之前,在上已关闭了某些内容.解决问题的第二项任务是什么?

class Dummy {  }

struct ContentView: View {
    @State private var dummy : Dummy?

    init() {
        print("Init") // In either case, is printed before "Body"

        // Using this assignment, "dummy is nil" shows on screen.
        self.dummy = Dummy()

        // Using this, "dummy is non-nil" shows on screen.
        // From https://stackoverflow.com/questions/61650040/swiftui-initializer-apparent-circularity
        // self._dummy = State(initialValue: Dummy())
    }

    var body: some View {
        print("Body")
        return ZStack {
            if dummy == nil {              // Decision seems to be taken
                Text("dummy is nil"    )   // before init() has finished.
            } else { 
                Text("dummy is non-nil") 
            }
        }
    }
}

解决方案

共识是它是一个看起来像bug的功能.

也在这里.亮点(为清晰起见进行了编辑)包括:

首先没有这样做的充分理由:

  • 您不应该在View初始化期间进行突变,因为该突变将出现在 body调用父视图

  • SwiftUI中的
  • @State变量不应从您通过初始化程序传递的数据中初始化;由于模型是在视图外部维护的,因此无法保证该值将被真正使用.

  • init期间不要尝试覆盖@State的初始值.

这是功能失常的"位置(接近我的位置):

  • 这是由于编译器的属性包装器实现中的人为限制.解决方法是直接通过_value
  • 初始化后备存储

说明方式和原因:

  • @State中的值将始终使用您在init中传递的值进行初始化,这是简单的Swift.但是,在下一个body调用之前,SwiftUI将调用update方法并在先前的值将覆盖init值的情况下重新注入一个值.

  • 这不是错误! :man_facepalming:它按预期/广告方式工作. ... @State属性包装器为视图添加了一种可能的突变方式,这也意味着该视图是私有的,而不是从父级初始化的,等等.

它是飞行员错误(可能是真的):

  • 这里没有问题,除了人们误解国家是为了什么而建.

TL;DR

It seems that the ContentView below evaluates the body's if statement before init has run. Is there a race condition, or is my mental model out-of-order?

Kudos

A shout-out to Asperi, who provided the state-initializer equivalent that solves today's problem.

Code

Why does ContentView display "dummy is nil"? It seems something gets closed over before the initializer sets dummy. What is it about the second assignment that fixes things?

class Dummy {  }

struct ContentView: View {
    @State private var dummy : Dummy?

    init() {
        print("Init") // In either case, is printed before "Body"

        // Using this assignment, "dummy is nil" shows on screen.
        self.dummy = Dummy()

        // Using this, "dummy is non-nil" shows on screen.
        // From https://stackoverflow.com/questions/61650040/swiftui-initializer-apparent-circularity
        // self._dummy = State(initialValue: Dummy())
    }

    var body: some View {
        print("Body")
        return ZStack {
            if dummy == nil {              // Decision seems to be taken
                Text("dummy is nil"    )   // before init() has finished.
            } else { 
                Text("dummy is non-nil") 
            }
        }
    }
}

解决方案

Consensus is that it's a feature that looks like a bug.

Helpful discussion in Swift Forum. Also here. Highlights (edited for clarity) include:

Good reasons not to do this in the first place:

  • You're not suppose to mutate during View init since that'd be in the body call of the parent view

  • @State variables in SwiftUI should not be initialized from data you pass down through the initializer; since the model is maintained outside of the view, there is no guarantee that the value will really be used.

  • Don't try overriding @State's initial value during init. It will only work once during very first view creation (important: not value initialization) and only if that view in the view tree gets re-created / replaced with a different view of the same type but internally different id.

The "it's a misfeature" position (close to mine):

  • This is due to an artificial limitation in the compiler's implementation of property wrappers. The workaround is to initialize the backing storage directly via _value

Explanations of how and why:

  • The value in @State will always be initialized with the value you pass in init, this is simple Swift. However before next body call SwiftUI will call update method and reinject a value into it if there was a previous value which will override your value from init.

  • This is NOT a bug! :man_facepalming: It works as expected/advertised. ... @State property wrapper adds one possible way of mutation to the view, which also is meant to be private to the view, not initialized from parent, etc.

It's pilot error (probably true):

  • There is NO problem here except people misunderstanding what State is build for.

这篇关于在初始化运行之前布局的SwiftUI视图(显然)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆