I'm trying to build a custom NavBar
with some optional Views
, like a searchbar (but only if the view needs to display it).
I need to pass some @State
properties with @Binding
down the views, basically. But I also need them to be Optional
parameters.
Here's an example:
struct NavBar: View { var isSearchable: Bool? @Binding var searchTxt: String @Binding var searchIsOn: Bool var navBarTitle: String var navBarAction: (() -> Void)? var navBarImage: String? init(navBarTitle: String, navBarAction: (() -> Void)? = nil, navBarImage: String? = nil, isSearchable: Bool? = false, searchTxt: (Binding<String>)?, searchIsOn : (Binding<Bool>)?) { self.navBarTitle = navBarTitle if(navBarAction != nil) { self.navBarAction = navBarAction! } if(navBarImage != nil) { self.navBarImage = navBarImage! } self.isSearchable = isSearchable self._searchTxt = (searchTxt != nil) ? (searchTxt!).binding : nil self._searchIsOn = (searchIsOn != nil) ? (searchIsOn!).binding : nil assert((navBarAction != nil) ? navBarImage != nil : true) assert((isSearchable! == true) ? (searchTxt!.value.count > 0) : true) } // var body .... }
The properties I'm talking about are searchIsOn
and searchTxt
. But doing the assignment self._searchTxt = searchTxt
or self._searchIsOn = searchIsOn
throws a compile error:
Cannot assign value of type 'Binding?' to type 'Binding'
Do you know how could I resolve this issue?
Or is there a better way to do what I'm trying to do?
Optional binding is a mechanism built into Swift to safely unwrap optionals. Since an optional may or may not contain a value, optional binding always has to be conditional. To enable this, conditional statements in Swift support optional binding, which checks if a wrapped value actually exists.
A binding in SwiftUI is a connection between a value and a view that displays and changes it. You can create your own bindings with the @Binding property wrapper, and pass bindings into views that require them.
String in swift is a value type, so your textValue property is taking a copy of the value, and SwiftUI is monitoring that copy, not the actual value in Controller.message . What you want here is a binding or an observed object—exactly which depends on whether Controller is a struct or a class type.
A binding connects a property to a source of truth stored elsewhere, instead of storing data directly. For example, a button that toggles between play and pause can create a binding to a property of its parent view using the Binding property wrapper.
@Binding var searchTxt: String? init(searchTxt: Binding<String?>?) { self._searchTxt = searchTxt ?? Binding.constant(nil) }
Update: I prefer this one. TextField("", text: $text ?? "default value")
https://stackoverflow.com/a/61002589/4728060
func ??<T>(lhs: Binding<Optional<T>>, rhs: T) -> Binding<T> { Binding( get: { lhs.wrappedValue ?? rhs }, set: { lhs.wrappedValue = $0 } ) }
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