在 SwiftUI 中创建电子邮件输入字段

Creating an Email Input Field in SwiftUI

我正在尝试在 SwiftUI 2 中创建一个仅允许输入特定字符的电子邮件输入字段。这里的代码是基于.

的一段代码

代码本身有效,但由于这是一个可重用的视图组件,我想提供一个文本 属性 从父视图的视图模型到 EmailTextField,它在文本输入更改时更新。 ..

EmailTextField 视图:

import SwiftUI
import Combine

struct EmailTextField: View {
    private class EmailTextFieldViewModel: ObservableObject {
        @Published var text = String.Empty
        private var subCancellable: AnyCancellable!
        private var validCharSet = CharacterSet(charactersIn: "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._-+$!~&=#[]@")

        init() {
            subCancellable = $text.sink {
                value in

                /* Check if the new string contains any invalid characters. */
                if value.rangeOfCharacter(from: self.validCharSet.inverted) != nil {
                    /* Clean the string (do this on the main thread to avoid overlapping with the current ContentView update cycle). */
                    DispatchQueue.main.async {
                        self.text = String(self.text.unicodeScalars.filter {
                            self.validCharSet.contains([=10=])
                        })
                    }
                }
            }
        }

        deinit {
            subCancellable.cancel()
        }
    }

    @ObservedObject private var viewModel = EmailTextFieldViewModel()
    private let placeHolder: String

    var body: some View {
        TextField(placeHolder, text: $viewModel.text)
            .keyboardType(.emailAddress)
            .autocapitalization(.none)
            .disableAutocorrection(true)
    }

    init(_ placeHolder: String = .Empty, text: Binding<String>) {
        self.placeHolder = placeHolder
    }
}

在父视图中,我尝试这样做:

var body: some View {
    VStack {
        EmailTextField("Email", text: $viewModel.email)
            .onChange(of: viewModel.email, perform: onEmailInputChanged)
    }
}

private func onEmailInputChanged(changedEmail: String) {
    // Nothing happens here!
    print("\(changedEmail)")
}

我需要如何更改 EmailTextField 代码才能将其视图模型中的 text 变量绑定到其构造函数中的 text: Binding<String> 参数?

这是您的(有点复杂的)代码的替代方法。我认为它更简单,提供可重用的 EmailInputView 和对 onEmailInputChanged 的调用,对我来说效果很好:

import SwiftUI
import Combine

class ViewModel: ObservableObject {
    @Published var email = ""
}

struct ContentView: View {
    @StateObject var viewModel = ViewModel()

    var body: some View {
        VStack {
            EmailInputView(placeHolder: "Email", txt: $viewModel.email)
                .onChange(of: viewModel.email, perform: onEmailInputChanged)
        }
    }
    
    private func onEmailInputChanged(changedEmail: String) {
        print("-----> in onEmailInputChanged: \(changedEmail) ")
    }
}

struct EmailInputView: View {
    var placeHolder: String = ""
    @Binding var txt: String
    
    var body: some View {
        TextField(placeHolder, text: $txt)
            .keyboardType(.emailAddress)
            .onReceive(Just(txt)) { newValue in
                let validString = newValue.filter { "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._-+$!~&=#[]@".contains([=10=]) }
                if validString != newValue {
                    self.txt = validString
                }
        }
    }
}