SwiftUI 通过实现 UIViewRepresentable 来使用 UIKit 组件

Posted

技术标签:

【中文标题】SwiftUI 通过实现 UIViewRepresentable 来使用 UIKit 组件【英文标题】:SwifUI using UIKit components by implementing UIViewRepresentable 【发布时间】:2019-12-17 09:15:20 【问题描述】:

好的,我知道如何使用 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 本地执行此操作的方式。

【问题讨论】:

【参考方案1】:

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

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

【讨论】:

仅链接答案不被视为良好做法。请看***.com/help/how-to-answer 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review @SuitBoyApps,感谢您指出。我已删除链接,但目前无法提供更完整的答案。

以上是关于SwiftUI 通过实现 UIViewRepresentable 来使用 UIKit 组件的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI - 与 Microsoft 的 Firebase 身份验证

为 SwiftUI Apple Watch App 实现基于页面的导航

SwiftUI完美弹出UIActivityViewController(通过微信QQ或隔空投送分享)的应用分享窗口

SwiftUI完美弹出UIActivityViewController(通过微信QQ或隔空投送分享)的应用分享窗口

SwiftUI 4.0 中原生图表(Charts)实现超长内容滚动功能

SwiftUI 4.0 中原生图表(Charts)实现超长内容滚动功能