How to Assign an Optional Binding Parameter in Swiftui

How to assign an optional Binding parameter in SwiftUI?

@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 }
)
}

Optional Binding in parameter SwiftUI

There is special Binding constructor for this purpose

SingleFileView(showSheetModifFile: Binding(self.$showModifFileSheet), 
fileToModify: Binding(self.$fileToModify))

Update: alternate solution

struct FileDemoView: View {
@State var showModifFileSheet : Bool? = false
@State var fileToModify : File? = File()

var body: some View {
SingleFileView(showSheetModifFile: $showModifFileSheet, fileToModify: $fileToModify)
}

}


struct SingleFileView: View {
@Binding var showSheetModifFile : Bool?
@Binding var fileToModify : File?


init(showSheetModifFile : Binding<Bool?> = .constant(nil), fileToModify : Binding<File?> = .constant(nil)) {
_showSheetModifFile = showSheetModifFile
_fileToModify = fileToModify
}

var body: some View {
Text("")
}
}

Optional chaining the binding value in SwiftUI textfield

When use the "dot chaining" syntax on a Binding its a shortcut syntax for creating a Binding. It doesn't have the same semantics "lookup a property of this thing" that the dot syntax usually holds.

So $food.name is not going to resolve the binding of food then reference one of its properties. It going to create a two-way Binding<String> to the name property of the food.

Similarly, when you have $food.macroProfile the value of that expression is a Binding<MacroNutrientProfile?>... a binding that will directly change a value in food (and the value it can change is an optional). It is not a resolution of the binding $food followed by referencing one of that object's properties.

$food.macroProfile?.carb is nonsensical because $food.macroProfile is of type Binding<MacroNutrientProfile?> which is not an optional type. So you see errors.

$food.name is not nonsensical because it is a Binding<String> and you're not trying to treat a non-optional value as an optional.

One way to change it is to use a custom binding:

struct Food: Codable, Identifiable {
var id = UUID()
var name: String = ""
var energy: Float?
var water: Float?
var macroProfile: MacronutrientProfile?
}

struct MacronutrientProfile: Codable {
var carb: Float?
var protein: Float?
var fat: Float?
}

struct SomeView : View {
@State var food: Food
let gram = NumberFormatter()

var body : some View {
let carbBinding = Binding<Float?>(get: { food.macroProfile?.carb },
set: { newValue in food.macroProfile?.carb = newValue })

return HStack {
Text("Carbohydrates")
Spacer()
TextField("Grams", value: carbBinding, formatter: gram)
.multilineTextAlignment(.trailing)
.keyboardType(.numberPad)
}
}
}

Optional State or Binding in SwiftUI

Here is working example of optional with State and Binding:



import SwiftUI

struct ContentView: View {

@State private var progress: Int?

var body: some View {

CustomView(progress: $progress)

}
}


struct CustomView: View {

@Binding var progress: Int?

var body: some View {

Button("update") {

if let unwrappedInt = progress { progress = unwrappedInt + 1 }
else { progress = 0 } //<< █ █ Here: initializing! █ █

}
.onChange(of: progress) { newValue in

if let unwrappedInt = progress { print(unwrappedInt) }

}

}
}

How to convert value of optional type Binding T? ? to Binding T? in SwiftUI?

The process of "converting" an optional to a non-optional is unwrapping. The only way you can convert an optional to a non-optional is to provide some default value in the case where the optional is nil.

You have got a little confused about Swift optional vs default parameters.

Your problem here is you want your height argument to be optional (as in the caller doesn't need to specify it), but you shouldn't declare it as an optional because your object needs there to be an instance of a Binding - the binding must exist even though its wrapped value may be nil.

The type wrapped in the binding is an optional Double and that is what you need to provide as a default value - a binding to a Double?. You can do this with Binding.constant

struct SomeView {
@Binding var height: Double?
init(height: Binding<Double?> = .constant(nil)) {
self._height = height
}
}

Now your default value is a binding to a Double? and the wrapped value is nil

If you did want your caller to be able to explicitly pass nil for the height binding then you have to deal with the default value in the initialiser itself:

struct SomeView {
@Binding var height: Double?
init(height: Binding<Double?>? = nil) {
self._height = height ?? .constant(nil)
}
}

SwiftUI Optional binding argument on View init

Use .constant binding, like

public init(selMode: String, isPresented:(Binding<Bool>) = .constant(true)) {
UITableView.appearance().separatorStyle = .none
_selMode = State(initialValue: selMode)
_isPresented = isPresented
}

How to optionally pass in a Binding in SwiftUI?

The main problem with what you want to achieve is that when the index is handled by the parent your View needs a @Binding to it, but when it handles the index itself it needs @State. There are two possible solutions.

If the view can ignore the index property when it doesn't have one:

struct ReusableView: View {

@Binding var index: Int?

init(_ index: Binding<Int?>) {
self._index = index
}

init() {
self._index = .constant(nil)
}

var body: some View {
VStack {
index.map { Text("The index is \($0)") }
}
}
}

The advantage is that it is very straightforward - just two initializers, but you cannot change the value of the index when it is handled by ResusableView itself (its a constant).

If the view cannot ignore the index property when it doesn't have one:

struct ReusableView: View {

private var content: AnyView

init(_ index: Binding<Int>? = nil) {
if let index = index {
self.content = AnyView(DependentView(index: index))
} else {
self.content = AnyView(IndependentView())
}
}

var body: some View {
content
}

private struct DependentView: View {

@Binding var index: Int

var body: some View {
Text("The index is \(index)")
}
}

private struct IndependentView: View {

@State private var index: Int = 0

var body: some View {
Text("The index is \(index)")
}
}

}

The clear advantage is that you have a view that can either be bound to a value or manage it as its own State. As you can see ReusableView is just a wrapper around two different views one managing its own @State and one being bound to @State of its parent view.



Related Topics



Leave a reply



Submit