如何在 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
。这是允许的。
您可以通过添加闭包参数使您的TextView
与Button
的初始化程序具有相似的形式:
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?的主要内容,如果未能解决你的问题,请参考以下文章