[英]Using optional binding when SwiftUI says no
TL;DR:我试图绑定到TextField
内部的String
嵌套在Optional
类型中,因此我无法直接执行此操作。 我已经尝试了下面列出的各种修复。
我是一个简单的人,我的用例也很简单——我希望能够使用 TextField 来编辑我的对象的名称。
困难的产生是因为object 可能不存在。
剥离代码,代码看起来像这样。
请注意,示例 View没有考虑Optional
struct Foo {
var name: String
}
extension Foo {
var sampleData: [Foo] = [
Foo(name: "Bar")
]
}
再次,在没有Optionals 的完美世界中,它看起来像这样
struct Ashwagandha: View {
@StateObject var ashwagandhaVM = AshwagandhaVM()
var body: some View {
TextField("", text: $ashwagandhaVM.currentFoo.name)
}
}
我故意不打开可选的包装,使currentFoo: Foo?
class AshwagandhaVM: ObservableObject {
@Published var currentFoo: Foo?
init() {
self.currentFoo = Foo.sampleData.first
}
}
以下是使TextField
和Foo.name
朋友的徒劳承诺,并带有相关错误。
TextField("", text: $ashwagandhaVM.currentFoo?.name)
进入添加/删除“?”/“!”的修复周期
TextField("Change chatBot's name", text: $(ashwagandhaVM.currentFoo..name)
“'$' 不是标识符;使用反引号将其转义”
TextField("", text: $ashwagandhaVM.currentFoo..name)
“无法强制解包非可选类型‘Binding<Foo?>’的值”
if let asparagus = ashwagandhaVM.currentFoo.name {
TextField("", text: $asparagus.name)
}
“在范围内找不到 $asparagus”
运气不好,因为String
嵌套在Optional
中; 我只是认为编辑String
不应该有那么多麻烦。
即为什么这个问题可能无关紧要
我正在重新学习 MVVM 的用法,尤其是如何使用嵌套数据类型。 我想检查在不为应用程序中每个 ViewModel 中的每个属性编写额外的 CRUD 层的情况下我能走多远。 如果您知道实现此目标的更好方法,请联系我。
问题评论中的人们给出了很好的建议。 不要这样做:更改您的视图 model 以提供一个非可选属性来代替。
但是......也许你被一个可选属性卡住了,出于某种原因你只需要绑定它。 在这种情况下,您可以创建一个Binding
并手动解包:
class MyModel: ObservableObject {
@Published var name: String? = nil
var nameBinding: Binding<String> {
Binding {
self.name ?? "some default value"
} set: {
self.name = $0
}
}
}
struct AnOptionalBindingView: View {
@StateObject var model = MyModel()
var body: some View {
TextField("Name", text: model.nameBinding)
}
}
这将使您绑定到文本字段。 如果支持属性为 nil,它将提供默认值。 如果支持属性更改,视图将重新呈现(只要它是您的@StateObject
或@ObservedObject
的@Published
属性)。
我认为你应该改变方法,保存的控制应该保留在 model 中,在视图中你应该只捕获新名称并拦截来自用户的保存按钮:
class AshwagandhaVM: ObservableObject {
@Published var currentFoo: Foo?
init() {
self.currentFoo = Foo.sampleData.first
}
func saveCurrentName(_ name: String) {
if currentFoo == nil {
Foo.sampleData.append(Foo(name: name))
self.currentFoo = Foo.sampleData.first(where: {$0.name == name})
}
else {
self.currentFoo?.name = name
}
}
}
struct ContentView: View {
@StateObject var ashwagandhaVM = AshwagandhaVM()
@State private var textInput = ""
@State private var showingConfirmation = false
var body: some View {
VStack {
TextField("", text: $textInput)
.padding()
.textFieldStyle(.roundedBorder)
Button("save") {
showingConfirmation = true
}
.padding()
.buttonStyle(.bordered)
.controlSize(.large)
.tint(.green)
.confirmationDialog("are you sure?", isPresented: $showingConfirmation, titleVisibility: .visible) {
Button("Yes") {
confirmAndSave()
}
Button("No", role: .cancel) { }
}
//just to check
if let name = ashwagandhaVM.currentFoo?.name {
Text("in model: \(name)")
.font(.largeTitle)
}
}
.onAppear() {
textInput = ashwagandhaVM.currentFoo?.name ?? "default"
}
}
func confirmAndSave() {
ashwagandhaVM.saveCurrentName(textInput)
}
}
更新
用整个结构做
struct ContentView: View {
@StateObject var ashwagandhaVM = AshwagandhaVM()
@State private var modelInput = Foo(name: "input")
@State private var showingConfirmation = false
var body: some View {
VStack {
TextField("", text: $modelInput.name)
.padding()
.textFieldStyle(.roundedBorder)
Button("save") {
showingConfirmation = true
}
.padding()
.buttonStyle(.bordered)
.controlSize(.large)
.tint(.green)
.confirmationDialog("are you sure?", isPresented: $showingConfirmation, titleVisibility: .visible) {
Button("Yes") {
confirmAndSave()
}
Button("No", role: .cancel) { }
}
//just to check
if let name = ashwagandhaVM.currentFoo?.name {
Text("in model: \(name)")
.font(.largeTitle)
}
}
.onAppear() {
modelInput = ashwagandhaVM.currentFoo ?? Foo(name: "input")
}
}
func confirmAndSave() {
ashwagandhaVM.saveCurrentName(modelInput.name)
}
}
struct ContentView: View {
@StateObject var store = Store()
var body: some View {
if let nonOptionalStructBinding = Binding($store.optionalStruct)
TextField("", text: nonOptionalStructBinding.name)
}
else {
Text("optionalStruct does not exist")
}
}
}
此外,SwiftUI 中的 MVVM 是一个坏主意,因为View
数据结构比视图 model object 更好。
我已经编写了几个不错的通用可选Binding
助手来解决这种情况。 请参阅此线程。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.