【问题标题】:How to fix TextField Binding <String> to show and edit in SwiftUI如何修复 TextField Binding <String> 以在 SwiftUI 中显示和编辑
【发布时间】:2021-05-19 04:50:31
【问题描述】:

我想在TextField 中显示文本并能够对其进行编辑。但是我在绑定它时遇到了问题。这是我的代码:

struct DataView: View {
@ObservedObject private var dataPresenter = DataPresenter()

var body: some View {
    VStack {
        TextField("Name", text: $dataPresenter.data?.name ?? "") // This is the error

    }
        .onAppear(perform: {
            dataPresenter.getData()
        })
     }
}

class DataPresenter: ObservableObject {
@Injected private var getDataInteractor: GetDataInteractor
@Injected private var cancellables: Set<AnyCancellable>
@Published var data: GetDataResp?

func getData() {
    getDataInteractor.execute()
        .sink { error in
            print("Error:", error)
        } receiveValue: { response in
            if response.errorCode == "00" {
                print(response)
                self.data = response
            }
        }
        .store(in: &cancellables)
     }
}

struct GetDataResp: Codable, Equatable {
     var name : String?
     var address : String?
     var email : String?

enum CodingKeys: String, CodingKey {
        case name = "name"
        case address = "address"
        case email = "email"
     }

init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        name = try values.decodeIfPresent(String.self, forKey: .name)
        address = try values.decodeIfPresent(String.self, forKey: .address)
        email = try values.decodeIfPresent(String.self, forKey: .email)      
    }
}

当我没有在$dataPresenter.data.name ?? "" 中使用? 时,我总是会收到这样的错误:

然后,当我用? 修复它时,我收到更多这样的错误:

我陷入了这个错误循环,使用force unwrap 会使应用程序崩溃。我有比名字更多的变量,所以我不能只在响应中self.name = response.name ?? ""。我该如何解决这个问题?

任何建议将不胜感激。

【问题讨论】:

    标签: ios swiftui binding textfield optional


    【解决方案1】:

    TextField 需要 Binding,而不仅仅是 String。因此,当您提供 "" 时,如果 Binding 为 nil,您最终会得到非等效类型(Binding&lt;String&gt; 与 String)。

    要解决这个问题,您可能需要创建一个自定义绑定来处理 data 是 nil 的情况。

    这是一种可能的解决方案:

    
    struct DataView: View {
        
        @ObservedObject private var dataPresenter = DataPresenter()
        
        var body: some View {
            VStack {
                Text(dataPresenter.data?.name ?? "Empty")
                TextField("Name", text: dataPresenter.nonOptionalNameBinding())
            }
        }
    }
    
    struct GetDataResp {
        var name : String
    }
    
    class DataPresenter: ObservableObject {
        @Published var data: GetDataResp?
        
        func nonOptionalNameBinding() -> Binding<String> {
            .init {
                return self.data?.name ?? ""
            } set: { newValue in
                if self.data == nil {
                    self.data = GetDataResp(name: newValue)
                } else {
                    self.data?.name = newValue
                }
            }
        }
    }
    

    您可能需要根据您的需要在set: 部分以不同方式处理这种情况。而且,当然,我删除了GetDataResp,我认为它比我那里的更详细,但这应该让你开始。


    更新,基于 cmets:

    
    struct GetDataResp: Codable, Equatable {
         var name : String?
         var address : String?
         var email : String?
    }
    
    struct DataView: View {
        @ObservedObject private var dataPresenter = DataPresenter()
        
        var body: some View {
            VStack {
                Text(dataPresenter.data?.name ?? "Empty")
                TextField("Name", text: dataPresenter.nonOptionalBinding(keyPath: \.name))
                TextField("Address", text: dataPresenter.nonOptionalBinding(keyPath: \.address))
                TextField("Email", text: dataPresenter.nonOptionalBinding(keyPath: \.email))
            }
        }
    }
    
    
    class DataPresenter: ObservableObject {
        @Published var data: GetDataResp?
        
        func nonOptionalBinding(keyPath: WritableKeyPath<GetDataResp,String?>) -> Binding<String> {
            .init {
                return self.data?[keyPath: keyPath] ?? ""
            } set: { newValue in
                if self.data == nil {
                    self.data = GetDataResp()
                }
                self.data?[keyPath: keyPath] = newValue
            }
        }
    }
    

    【讨论】:

    • 我仍然被困在这里,因为GetDataResp 已经回复了姓名、地址、日期、电子邮件等。我不能做else 部分。当我添加一个参数时,它不能分配,因为它是一个 let 常量。 func nonOptionalNameBinding(data: String?) -&gt; Binding&lt;String&gt; { .init { return data ?? "" } set: { newValue in if self.dataMerchant == nil { print("empty", newValue) } else { data = newValue } } 我希望我可以使用DataView 中的参数,这样我就可以根据需要创建文本字段
    • 不太清楚您所说的“我希望我可以使用 DataView 中的参数,以便我可以根据需要创建文本字段”是什么意思。如果你想让它成为非可选的,GetDataResp 将不得不是非可选的。但是,如果你保持它是可选的,你将不得不做这样的事情。如果您包含 GetDataResp 的代码(在您的原始问题中 - cmets 中的代码效果不佳),我可以尝试向您展示如何使用它。
    • 嘿伙计,刚刚更新了GetDataResp,检查我的代码。谢谢大佬
    • 看起来不会改变我对我的回答。我仍然不知道您评论中的代码在做什么。例如,dataMerchat 是什么?您的问题中从未提及。
    • 好吧,在你的方法中,else 是:self.data?.name = newValue。但是,除了名称之外,我还有其他几个变量,例如地址、电子邮件等。我的意思是,当您在 DataView 中使用 self.data?.name = newValue 部分时,它是硬编码的。如何分配self.data?.address = newValue 等?您是在告诉我为 1 个变量创建 1 个方法吗?希望我能让你清楚我还在挣扎什么。感谢您的回答
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-17
    • 1970-01-01
    • 2021-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多