如何在 SwiftUI 中使用 inout 而不是 Binding?

Posted

技术标签:

【中文标题】如何在 SwiftUI 中使用 inout 而不是 Binding?【英文标题】:How can I use inout instead of Binding in SwiftUI? 【发布时间】:2021-04-04 09:32:06 【问题描述】:

我想知道我是否可以通过 inout 而不是 Binding 使我的视图函数绑定自身,是否有可能为我们提供正确/更好的语法?知道我收到以下编译时错误:

Modifying state during view update, this will cause undefined behavior.

这对我来说是可以理解的,我想也许我在这项工作中使用了错误的语法。

PS:我完全了解 Binding 及其用例,如果我们也可以使用 inout,这个问题尝试找到答案。


func TextView(inPutValue: inout Bool) -> some View 

    return Text("Hello")
        .onTapGesture  inPutValue.toggle() 
    


用例:

struct ContentView: View 

    @State private var isOn: Bool = true

    var body: some View 
        
        TextView(inPutValue: &isOn)
 
    
    



更新:

import SwiftUI

struct ContentView: View 

    @State private var value: Int = Int()  didSet  print(value.description)  

    var body: some View 

        Button("update State via inout")  inoutFunction(incomingValue: &value) 
        
    



func inoutFunction(incomingValue: inout Int)  incomingValue += 1 

【问题讨论】:

【参考方案1】:

您不能在这里使用inout 的原因是因为inout 具有copy-in-copy-out 行为。该值作为副本传递给函数,一旦函数返回,修改后的副本就会被复制回来。您不应该将其视为传递引用,而是认为可以通过这种方式实现优化。

现在知道了,Swift 编译器禁止你在转义闭包中使用 inout 参数是很有意义的,例如在 onTapGesture 中使用 inPutValue。毕竟,修改后的inPutValue 仅在TextView 返回时才被复制回来,而不是在有人点击它时,因此您在onTapGesture 中对其进行的任何修改对于TextView 的调用者来说根本不可见。另见this answer。

因此,一旦TextView 返回,该值就会被复制回调用者。如错误消息所述,不允许在计算 body(即“视图更新期间”)时直接修改 @State

现在我们来看看Button的情况。请注意,这一次,对inoutFunction(incomingValue: &value) 的调用发生在按钮的单击处理程序中,而不是在body 中——这意味着当按下按钮时将写入value。这是允许的。

您可以通过添加闭包参数使您的TextViewButton 的初始化程序具有相似的形式:

func TextView(update: @escaping () -> Void) -> some View 

    return Text("Hello")
        .onTapGesture(perform: update)
    

注意这里根本没有inout,就像Button.init中没有inout一样。

然后您可以使用inout 参数编写您的函数:

func inoutFunction(_ b: inout Bool) 
    b.toggle()

并使用它:

@State private var isOn = true

var body: some View 
    TextView  inoutFunction(incomingValue: &isOn) 

请注意,您根本不需要 inout

TextView  isOn.toggle() 

【讨论】:

哇!非常感谢,我就像您看到我的观点一样,为什么我期望我的代码可以工作,因为 onTapGesture 正在更改正文中未用作渲染参考的值,这就是我这样做的原因应该可以工作,因为我刚刚将一个状态传递给 TextView,这在正文中根本没有任何关系,但是在你解释之后,这一切现在对我来说都可以理解了。 这不是改变游戏规则吗?通过这种方式,我们可以直接进行修改,而无需在两者之间使用 getter 和 setter,你不这么认为吗? 我不确定我是否理解。你在说什么“方式”? @swiftPunk 你的回答我的意思是,我们也可以用普通的绑定方式! @swiftPunk 通过绑定,您可以抽象出toggle 调用,即调用者不需要每次都编写isOn.toggle() 调用。调用者只是传递$isOn,被调用者知道如何处理(应该设置什么值)。

以上是关于如何在 SwiftUI 中使用 inout 而不是 Binding?的主要内容,如果未能解决你的问题,请参考以下文章