使自定义SwiftUI视图适应内置修饰符

时间:2020-03-06 06:18:45

标签: uikit uitextfield swiftui

我正在努力为SwiftUI编写自己的BetterTextField视图,因为内置的TextField在几个方面都缺乏。即,我想支持延迟绑定(仅在定位焦点时更新绑定值,而不是在每次按键后强制重画),程序化聚焦/响应器控制以及SwiftUI缺少的UIKit UITextField的其他一些功能。 / p>

因此,我创建了一个自定义UIViewRepresentable,其中的协调者为UITextFieldDelegate,这很好用。但是,为了与其他视图保持一致,我真的很想让我的自定义文本字段适应某些现有的SwiftUI修饰符。

例如:

// Here's my content view
struct ContentView: View {
    var body: some View {
        BetterTextField("Username", text: $username)
            // I want to adapt the view to this modifier
            .textFieldStyle(RoundedBorderTextFieldStyle())
    }
}

// Here's my (simplified) custom text field view
struct BetterTextField: UIViewRepresentable {
    var title: String
    @Binding var text: String

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

    func makeUIView(context: Context) -> UITextField {
        let textField = UITextField()
        textField.placeholder = title
        return textField
    }

    func updateUIView(_ view: UITextField, context: Context) {
        view.text = text

        // How can I check for the .textFieldStyle() modifier here and set the corresponding UIKit style accordingly?
        view.borderStyle = .roundedRect
    }
}

正如评论所说,如何调整borderStyle的{​​{1}}属性以匹配“视图”修饰符?

更笼统地说,如何检查修饰符的存在并返回适当样式的自定义视图(例如UITextField转换为属性文本)?

2 个答案:

答案 0 :(得分:1)

View修饰符只是再次返回some View的函数,因此您可以实现对任何修饰符的支持,并符合您决定适合自定义类型的任何协议。您的控件在每个已实现的修饰符上的行为方式取决于您。

以下是对textFieldStyle修饰符的简单演示支​​持,使您的ContentView渲染BetterTextField的目的取决于添加的圆角矩形样式修饰符或已删除。

struct BetterTextField: UIViewRepresentable {
    var title: String
    @Binding var text: String

    private let textField = UITextField()

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

    func makeUIView(context: Context) -> UITextField {
        textField.placeholder = title
        return textField
    }

    func updateUIView(_ view: UITextField, context: Context) {
        view.text = text
    }
}

extension BetterTextField {
    func textFieldStyle<S>(_ style: S) -> some View where S : TextFieldStyle {
        if style is RoundedBorderTextFieldStyle {
            self.textField.borderStyle = .roundedRect
        }
        return self
    }
}

答案 1 :(得分:0)

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension View {

    /// Sets the style for `TextField` within the environment of `self`.
    public func textFieldStyle<S>(_ style: S) -> some View where S : TextFieldStyle

}

查看注释

TextField环境中设置self的样式

UIViewRepresentable继承自View,但“自身”中没有任何TextField

.bold,.italic ...是Font的修饰符,而不是通用View的修饰符。说

Image("image001").italic()

效果不好。

有关反跳的信息,请参见Debounced Property Wrapper

有关“延迟”绑定,请参见

/// Creates an instance with a `Text` label generated from a localized title
    /// string.
    ///
    /// - Parameters:
    ///     - titleKey: The key for the localized title of `self`, describing
    ///       its purpose.
    ///     - text: The text to be displayed and edited.
    ///     - onEditingChanged: An `Action` that will be called when the user
    ///     begins editing `text` and after the user finishes editing `text`,
    ///     passing a `Bool` indicating whether `self` is currently being edited
    ///     or not.
    ///     - onCommit: The action to perform when the user performs an action
    ///     (usually the return key) while the `TextField` has focus.
    public init(_ titleKey: LocalizedStringKey, text: Binding<String>, onEditingChanged: @escaping (Bool) -> Void = { _ in }, onCommit: @escaping () -> Void = {})

“延迟”绑定的示例

import SwiftUI
struct MyTextField<S>: View  where S: StringProtocol {
    let label: S
    @State private var __text = ""
    @Binding var text: String
    var body: some View {
        TextField(label, text: $__text, onEditingChanged: { (e) in

        }) {
            self.text = self.__text
        }
    }
}

struct ContentView: View {
    @State var text = " "
    var body: some View {
        VStack {
            MyTextField(label: "label", text: $text).textFieldStyle(RoundedBorderTextFieldStyle())
            Text(text)
        }.padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

enter image description here

如果您需要不同的字体和.bold,请使用

MyTextField(label: "label", text: $text).textFieldStyle(RoundedBorderTextFieldStyle()).font(Font.title.bold())

MyTextField(label: "label", text: $text).font(Font.title.bold()).textFieldStyle(RoundedBorderTextFieldStyle())