3

好的,我知道如何使用 UIViewRepresentable 和 Coordinator 包装 UIKit 组件(如 TextField)与 SwiftUI 一起使用。这一切都运作良好。但是,如何以 SwiftUI 的原生方式(即使用修饰符)从 SwiftUI 的角度自定义这些组件?

我有 UITextField 的示例包装器来启用一些额外的委托方法处理。这与将闭包传递给构造函数的本地方法类似。但是我如何使用 SwiftUI 修饰符对这个 UITextField 应用一些样式呢?

struct PasswordField: UIViewRepresentable {

    @Binding var text: String
    @Binding var isSecured: Bool

    let onBeginEditing: () -> Void
    let onEndEditing: () -> Void
    let onEditingChanged: (Bool) -> Void
    let onCommit: () -> Void


    init(text: Binding<String>, isSecured : Binding<Bool> = .constant(true),
         onBeginEditing: @escaping () -> Void = { },
         onEndEditing: @escaping () -> Void = { },
         onEditingChanged: @escaping (Bool) -> Void = { _ in },
         onCommit: @escaping () -> Void = { }) {

        self._text = text
        self._isSecured = isSecured
        self.onBeginEditing = onBeginEditing
        self.onEndEditing = onEndEditing
        self.onEditingChanged = onEditingChanged
        self.onCommit = onCommit
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(onBeginEditing: onBeginEditing, onEndEditing: onEndEditing, onEditingChanged: onEditingChanged, onCommit: onCommit)
    }

    func makeUIView(context: UIViewRepresentableContext<PasswordField>) -> UITextField {

        let textField = UITextField()
        textField.delegate = context.coordinator
        textField.addTarget(context, action: #selector(Coordinator.valueChanged(sender:)), for: .valueChanged)
        return textField
    }

    func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<PasswordField>) {

        uiView.text = text
        uiView.isSecureTextEntry = isSecured
    }
}

// MARK: - Coordinator
extension PasswordField {

    class Coordinator: NSObject, UITextFieldDelegate {

        let onBeginEditing: () -> Void
        let onEndEditing: () -> Void
        let onEditingChanged: (Bool) -> Void
        let onCommit: () -> Void

        init(onBeginEditing: @escaping () -> Void,
             onEndEditing: @escaping () -> Void,
             onEditingChanged: @escaping (Bool) -> Void,
             onCommit: @escaping () -> Void) {
            self.onBeginEditing = onBeginEditing
            self.onEndEditing = onEndEditing
            self.onEditingChanged = onEditingChanged
            self.onCommit = onCommit
        }

        @objc func valueChanged(sender: UITextField) {
            onEditingChanged(true)
        }

        func textFieldShouldReturn(_ textField: UITextField) -> Bool {
            textField.resignFirstResponder()
            onCommit()
            return true
        }

        func textFieldDidBeginEditing(_ textField: UITextField) {
            onBeginEditing()
            onEditingChanged(true)
        }

        func textFieldDidEndEditing(_ textField: UITextField) {
            onEditingChanged(false)
            onEndEditing()
        }
    }
}

struct PasswordField_Previews: PreviewProvider {
    static var previews: some View {
        PasswordField(text: .constant("PaSSword"), isSecured: .constant(true))
    }
}

现在我想像这样使用它:

PasswordField(text: self.$validator.value, onEditingChanged: { editing in
                            self.onEditingChanged?(editing)
                            self.isEdited = true
                        }, onCommit: {
                            self.validator.validateField()
                            self.validator.validateFieldAsync()
                        })
                        .font(.custom("AvenirNext-Light", size: 13)) // THIS IS IMPORTANT
                        .foregroundColor(Color("BodyText")) // THIS IS IMPORTANT
                        .frame(maxHeight: geometry.size.height)
                        .offset(x: 0, y: 16)
                        .padding(10)

好的,我知道我可以在包装器中硬编码这种风格,或者通过构造函数传递它。是的,但这不是 SwiftUI 本地执行此操作的方式。

4

1 回答 1

-1

您需要实现自己的自定义修饰符。请参阅 ViewModifier 协议的文档,尽管 SwiftUI 文档仍然很薄。

但是,您不必实现自己的 TextField 来处理密码输入,因为 SwiftUI 现在原生支持您想要的:SecureField。

于 2020-02-18T14:40:11.943 回答