I like to know if I could make my view function bind itself via inout instead of Binding, is it possible in right/better syntax for us? right know I am getting compile time error of:
Modifying state during view update, this will cause undefined behavior.
which is understandable for me, I thought maybe I am using-wrong syntax for this work.
PS: I completely know about Binding and it use case, this question try find answer if we could do it with inout as well.
func TextView(inPutValue: inout Bool) -> some View {
return Text("Hello")
.onTapGesture { inPutValue.toggle() }
}
use case:
struct ContentView: View {
@State private var isOn: Bool = true
var body: some View {
TextView(inPutValue: &isOn)
}
}
update:
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 }
The reason why you can't use inout
here is because inout
has a copy-in-copy-out behaviour. The value is passed into the function as a copy, and as soon as the function returns, the modified copy is copied back. You should not think of it as pass-by-reference, thought it can be implemented this way as an optimisation.
Now knowing that, it'd make a lot of sense for the Swift compiler to forbid you from using an inout parameter in an escaping closure, such as using inPutValue
in onTapGesture
. After all, the modified inPutValue
is only copied back when TextView
returns, not when someone taps it, so whatever modifications you do to it in onTapGesture
is not visible to the caller of TextView
at all. See also this answer.
So the value is copied back to the caller as soon as TextView
returns. As the error message says, modifying a @State
directly when computing body
(i.e. "during view update") is not allowed.
Now let's look at the case of Button
. Note that this time, the call to inoutFunction(incomingValue: &value)
happens inside the button's click handler, rather than in body
- meaning value
will be written to when the button is pressed. This is allowed.
You can make your TextView
to be of a similar form to Button
's initialiser by adding a closure argument:
func TextView(update: @escaping () -> Void) -> some View {
return Text("Hello")
.onTapGesture(perform: update)
}
Note that there is nothing inout
in this at all, just like there is nothing inout
in Button.init
.
You can then write your function with an inout
parameter:
func inoutFunction(_ b: inout Bool) {
b.toggle()
}
and use it:
@State private var isOn = true
var body: some View {
TextView { inoutFunction(incomingValue: &isOn) }
}
Notice that you don't need inout
at all.
TextView { isOn.toggle() }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With