SwiftUI 可选文本字段
SwiftUI Optional TextField
SwiftUI 文本字段可以与可选绑定一起使用吗?当前此代码:
struct SOTestView : View {
@State var test: String? = "Test"
var body: some View {
TextField($test)
}
}
产生以下错误:
Cannot convert value of type 'Binding< String?>' to expected argument type 'Binding< String>'
有什么办法解决这个问题吗?在数据模型中使用 Optionals 是一种非常常见的模式——事实上它是 Core Data 中的默认模式,所以 SwiftUI 不支持它们似乎很奇怪
最终 API 不允许这样做 - 但有一个非常简单且通用的解决方法:
extension Optional where Wrapped == String {
var _bound: String? {
get {
return self
}
set {
self = newValue
}
}
public var bound: String {
get {
return _bound ?? ""
}
set {
_bound = newValue.isEmpty ? nil : newValue
}
}
}
这允许您保留可选的同时使其与绑定兼容:
TextField($test.bound)
是的,目前 TextField
在 SwiftUI 中只能绑定到 String
个变量,不能 String?
。
但是您始终可以像这样定义自己的 Binding
:
import SwiftUI
struct SOTest: View {
@State var text: String?
var textBinding: Binding<String> {
Binding<String>(
get: {
return self.text ?? ""
},
set: { newString in
self.text = newString
})
}
var body: some View {
TextField("Enter a string", text: textBinding)
}
}
基本上,您将 TextField
文本值绑定到这个新的 Binding<String>
绑定,绑定将它重定向到您的 String?
@State 变量。
您可以添加此运算符重载,然后它就像不是 Binding 一样自然地工作。
func ??<T>(lhs: Binding<Optional<T>>, rhs: T) -> Binding<T> {
Binding(
get: { lhs.wrappedValue ?? rhs },
set: { lhs.wrappedValue = [=10=] }
)
}
这将创建一个绑定,如果运算符的值不为零,则 returns 左侧,否则 returns 右侧的默认值。
设置时只设置lhs值,忽略与右手边有关的任何内容。
可以这样使用:
TextField("", text: $test ?? "default value")
试试这个对我有用的可重用功能
@State private var name: String? = nil
private func optionalBinding<T>(val: Binding<T?>, defaultVal: T)-> Binding<T>{
Binding<T>(
get: {
return val.wrappedValue ?? defaultVal
},
set: { newVal in
val.wrappedValue = newVal
}
)
}
// Usage
TextField("", text: optionalBinding(val: $name, defaultVal: ""))
我更喜欢 provided by @Jonathon.,因为它简单而优雅,并且在 Optional
是 .none
(= nil
)而不是.some
.
但是我觉得值得在这里加上我的两分钱。我通过阅读 Jim Dovey 在 SwiftUI Bindings with Core Data 上的博客学习了这项技术。它与@Jonathon 提供的答案基本相同。但确实包含一个很好的模式,可以为许多不同的数据类型复制。
首先在 Binding
上创建一个扩展
public extension Binding where Value: Equatable {
init(_ source: Binding<Value?>, replacingNilWith nilProxy: Value) {
self.init(
get: { source.wrappedValue ?? nilProxy },
set: { newValue in
if newValue == nilProxy { source.wrappedValue = nil }
else { source.wrappedValue = newValue }
}
)
}
}
然后像这样在你的代码中使用...
TextField("", text: Binding($test, replacingNilWith: String()))
或
TextField("", text: Binding($test, replacingNilWith: ""))
SwiftUI 文本字段可以与可选绑定一起使用吗?当前此代码:
struct SOTestView : View {
@State var test: String? = "Test"
var body: some View {
TextField($test)
}
}
产生以下错误:
Cannot convert value of type 'Binding< String?>' to expected argument type 'Binding< String>'
有什么办法解决这个问题吗?在数据模型中使用 Optionals 是一种非常常见的模式——事实上它是 Core Data 中的默认模式,所以 SwiftUI 不支持它们似乎很奇怪
最终 API 不允许这样做 - 但有一个非常简单且通用的解决方法:
extension Optional where Wrapped == String {
var _bound: String? {
get {
return self
}
set {
self = newValue
}
}
public var bound: String {
get {
return _bound ?? ""
}
set {
_bound = newValue.isEmpty ? nil : newValue
}
}
}
这允许您保留可选的同时使其与绑定兼容:
TextField($test.bound)
是的,目前 TextField
在 SwiftUI 中只能绑定到 String
个变量,不能 String?
。
但是您始终可以像这样定义自己的 Binding
:
import SwiftUI
struct SOTest: View {
@State var text: String?
var textBinding: Binding<String> {
Binding<String>(
get: {
return self.text ?? ""
},
set: { newString in
self.text = newString
})
}
var body: some View {
TextField("Enter a string", text: textBinding)
}
}
基本上,您将 TextField
文本值绑定到这个新的 Binding<String>
绑定,绑定将它重定向到您的 String?
@State 变量。
您可以添加此运算符重载,然后它就像不是 Binding 一样自然地工作。
func ??<T>(lhs: Binding<Optional<T>>, rhs: T) -> Binding<T> {
Binding(
get: { lhs.wrappedValue ?? rhs },
set: { lhs.wrappedValue = [=10=] }
)
}
这将创建一个绑定,如果运算符的值不为零,则 returns 左侧,否则 returns 右侧的默认值。
设置时只设置lhs值,忽略与右手边有关的任何内容。
可以这样使用:
TextField("", text: $test ?? "default value")
试试这个对我有用的可重用功能
@State private var name: String? = nil
private func optionalBinding<T>(val: Binding<T?>, defaultVal: T)-> Binding<T>{
Binding<T>(
get: {
return val.wrappedValue ?? defaultVal
},
set: { newVal in
val.wrappedValue = newVal
}
)
}
// Usage
TextField("", text: optionalBinding(val: $name, defaultVal: ""))
我更喜欢 Optional
是 .none
(= nil
)而不是.some
.
但是我觉得值得在这里加上我的两分钱。我通过阅读 Jim Dovey 在 SwiftUI Bindings with Core Data 上的博客学习了这项技术。它与@Jonathon 提供的答案基本相同。但确实包含一个很好的模式,可以为许多不同的数据类型复制。
首先在 Binding
public extension Binding where Value: Equatable {
init(_ source: Binding<Value?>, replacingNilWith nilProxy: Value) {
self.init(
get: { source.wrappedValue ?? nilProxy },
set: { newValue in
if newValue == nilProxy { source.wrappedValue = nil }
else { source.wrappedValue = newValue }
}
)
}
}
然后像这样在你的代码中使用...
TextField("", text: Binding($test, replacingNilWith: String()))
或
TextField("", text: Binding($test, replacingNilWith: ""))