【问题标题】:How can I use inout instead of Binding in SwiftUI?如何在 SwiftUI 中使用 inout 而不是 Binding?
【发布时间】: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 }

【问题讨论】:

    标签: swift swiftui


    【解决方案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,被调用者知道如何处理(应该设置什么值)。
    猜你喜欢
    • 1970-01-01
    • 2020-07-10
    • 1970-01-01
    • 1970-01-01
    • 2020-10-25
    • 1970-01-01
    • 2021-07-14
    • 2019-11-29
    • 1970-01-01
    相关资源
    最近更新 更多