运用 Binding 的时分咱们经常会遇到需求绑定一个 Optional value 的状况。比如一个用于输入用户名的 TextField,假如是老用户就直接显示已有的用户名,新用户则为空。
struct BindingOptional: View {
@State var userName: String? = nil
var body: some View {
TextField("UserName", text: $userName)
}
}
这样写毫无疑问是要报错的,因为 Binding 的目标有必要是一个确定的值。
可是假如特意再声明一个 @State 做为中继,则繁琐许多。假如这样做代码大概要这么写:
struct BindingOptional: View {
@State var userName: String? = nil
@State var userNameTemp: String = "none"
var body: some View {
TextField("UserName", text: $userNameTemp)
.onChange(of: userNameTemp) {
self.userName = userNameTemp
}
}
}
因而找到一种办法能够便当针对 optional value的状况供给一个默认值很有必要。
自界说一个 Binding
@Binding 本质上是一个 property wrapper,最底层的思路就是咱们把 Binding<T?>
封装成 Binding<T>
回来。
实现的代码如下:
struct BindingOptional: View {
@State var userName: String? = nil
private var userNameTemp: Binding<String> {
Binding(get: {
"none"
}, set: { newValue in
self.userName = newValue
})
}
var body: some View {
TextField("UserName", text: userNameTemp)
}
}
咱们能够在 get 闭包中回来默认值。
更进一步,封装成办法
可是这样写还是有点繁琐,咱们能够把这个封装的过程界说为 Binding 的一个扩展办法。
extension Binding {
func defaultValue<T>(_ value: T) -> Binding<T> where Value == Optional<T> {
Binding<T> {
wrappedValue ?? value
} set: {
wrappedValue = $0
}
}
}
咱们就能够这样调用:
var body: some View {
TextField("UserName", text: $userName.defaultValue("none"))
}
考虑到咱们最常运用的是字符串,因而咱们能够针对字符串封装一个默认值是空字符的办法。
extension Binding where Value == Optional<String> {
public var orEmpty: Binding<String> {
Binding<String> {
wrappedValue ?? ""
} set: {
wrappedValue = $0
}
}
}
这样咱们运用的时分就会更简便一些:
var body: some View {
TextField("UserName", text: $userName.orEmpty)
}
自界说运算符
除了界说成一个办法,也能够通过自界说运算符实现。因为 ??
运算符的语义本来就代表供给默认值。假如想要更简洁一点把这个办法界说成 binding 的运算符也很适宜。
extension Binding {
static func ?? <T>(optional: Self, defaultValue: T) -> Binding<T> where Value == Optional<T> {
Binding<T>(
get: { optional.wrappedValue ?? defaultValue },
set: { optional.wrappedValue = $0 }
)
}
}
咱们就能够这样调用:
var body: some View {
TextField("UserName", text: $userName ?? "none")
}