Reputation: 119292
Imagine a view with some @Binding
variables:
init(isEditing: Binding<Bool>, text: Binding<Bool>)
How can we have the selection
working with an internal @State
if it is not provided in the initializer?
init(text: Binding<Bool>)
This is how to make TextField become first responder in SwiftUI
Note that I know we can pass a constant
like:
init(isEditing: Binding<Bool> = .constant(false), text: Binding<Bool>)
This will kill the dynamicity of the variable and it won't work as desire. Imagine re-inventing the isFirstResponder
of the UITextField
.
.constant(false)
. The keyboard will be gone on each view update..constant(true)
. The view will take the keyboard on each view update.Maybe! Apple is doing it somehow with TabView
.
Upvotes: 4
Views: 558
Reputation: 54466
You may create separate @State
and @Binding
properties and sync them using onChange
or onReceive
:
struct TestView: View {
@State private var selectionInternal: Bool
@Binding private var selectionExternal: Bool
init() {
_selectionInternal = .init(initialValue: false)
_selectionExternal = .constant(false)
}
init(selection: Binding<Bool>) {
_selectionInternal = .init(initialValue: selection.wrappedValue)
_selectionExternal = selection
}
var body: some View {
if #available(iOS 14.0, *) {
Toggle("Selection", isOn: $selectionInternal)
.onChange(of: selectionInternal) {
selectionExternal = $0
}
} else {
Toggle("Selection", isOn: $selectionInternal)
.onReceive(Just(selectionInternal)) {
selectionExternal = $0
}
}
}
}
struct ContentView: View {
@State var selection = false
var body: some View {
VStack {
Text("Selection: \(String(selection))")
TestView(selection: $selection)
TestView()
}
}
}
Upvotes: 3
Reputation: 1782
One solution is to pass an optional binding and use a local state variable if the binding is left nil
. This code uses a toggle as an example (simpler to explain) and results in two interactive toggles: one being given a binding and the other using local state.
import SwiftUI
struct ContentView: View {
@State private var isOn: Bool = true
var body: some View {
VStack {
Text("Special toggle:")
SpecialToggle(isOn: $isOn)
.padding()
SpecialToggle()
.padding()
}
}
}
struct SpecialToggle: View {
/// The binding being passed from the parent
var isOn: Binding<Bool>?
/// The fallback state if the binding is left `nil`.
@State private var defaultIsOn: Bool = true
/// A quick wrapper for accessing the current toggle state.
var toggleIsOn: Bool {
return isOn?.wrappedValue ?? defaultIsOn
}
init(isOn: Binding<Bool>? = nil) {
if let isOn = isOn {
self.isOn = isOn
}
}
var body: some View {
Toggle(isOn: isOn ?? $defaultIsOn) {
Text("Dynamic label: \(toggleIsOn.description)")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Upvotes: 2