Можно ли добавить кернинг в TextField в SwiftUI? - PullRequest
1 голос
/ 05 ноября 2019

Чтобы соответствовать стилегиду, я должен добавить кернинг в текстовое поле, как заполнитель, так и само значение.

С помощью UIKit я смог сделать это с помощью:

    class MyTextField: UITextField {
    override func awakeFromNib() {
        super.awakeFromNib()

        // ...

        self.attributedPlaceholder = NSAttributedString(string: self.placeholder ?? "", attributes: [
            //...
            NSAttributedString.Key.kern: 0.3
        ])

        self.attributedText = NSAttributedString(string: "", attributes: [
            // ...
            NSAttributedString.Key.kern: 0.3
        ])
    }
}

В SwiftUI я мог выяснить, что я могу применить эффект кернинга к элементу Text, например так:

    Text("My label with kerning")
       .kerning(0.7)

К сожалению, я не смог найти способ применить стиль кернинга ни к значению TextField, ни к заполнителю. Есть идеи на этот счет? Заранее спасибо

1 Ответ

2 голосов
/ 05 ноября 2019

Существует простое руководство по HackingwithSwift , которое показывает, как реализовать UITextView. Его можно легко адаптировать для UITextField.

Вот краткий пример, показывающий, как использовать UIViewRepresentable для вас UITextField. Установка кернинга как для текста, так и для заполнителя.

struct ContentView: View {

    @State var text = ""

    var body: some View {
        MyTextField(text: $text, placeholder: "Placeholder")
    }

}

struct MyTextField: UIViewRepresentable {
    @Binding var text: String
    var placeholder: String

    func makeUIView(context: Context) -> UITextField {
        return UITextField()
    }

    func updateUIView(_ uiView: UITextField, context: Context) {
        uiView.attributedPlaceholder = NSAttributedString(string: self.placeholder, attributes: [
            NSAttributedString.Key.kern: 0.3
        ])
        uiView.attributedText = NSAttributedString(string: self.text, attributes: [
            NSAttributedString.Key.kern: 0.3
        ])
    }
}

Обновление

Вышеприведенное не работает для установки кернинга на attribuText. Заимствуя из фантастической работы, проделанной Костантино Пистанья в его средней статье , нам нужно сделать немного больше работы.

Во-первых, нам нужно создать упакованную версию UITextField, которая дает нам доступ к методам делегатов.

class WrappableTextField: UITextField, UITextFieldDelegate {
    var textFieldChangedHandler: ((String)->Void)?
    var onCommitHandler: (()->Void)?

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        if let nextField = textField.superview?.superview?.viewWithTag(textField.tag + 1) as? UITextField {
            nextField.becomeFirstResponder()
        } else {
            textField.resignFirstResponder()
        }
        return false
    }

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if let currentValue = textField.text as NSString? {
            let proposedValue = currentValue.replacingCharacters(in: range, with: string)
            print(proposedValue)
            self.attributedText = NSAttributedString(string: currentValue as String, attributes: [
                NSAttributedString.Key.kern: 10
            ])
            textFieldChangedHandler?(proposedValue as String)
        }
        return true
    }

    func textFieldDidEndEditing(_ textField: UITextField) {
        onCommitHandler?()
    }
}

Поскольку метод делегата shouldChangeCharactersIn будет вызываться при каждом изменении текста, мы должны использовать его для обновления значения attributedText. Сначала я попытался использовать proposedVale, но он удвоил бы символы, он работал бы как ожидалось, если бы мы использовали currentValue

Теперь мы можем использовать WrappedTextField в UIViewRepresentable.

struct SATextField: UIViewRepresentable {
    private let tmpView = WrappableTextField()

    //var exposed to SwiftUI object init
    var tag:Int = 0
    var placeholder:String?
    var changeHandler:((String)->Void)?
    var onCommitHandler:(()->Void)?

    func makeUIView(context: UIViewRepresentableContext<SATextField>) -> WrappableTextField {
        tmpView.tag = tag
        tmpView.delegate = tmpView
        tmpView.placeholder = placeholder
        tmpView.attributedPlaceholder = NSAttributedString(string: self.placeholder ?? "", attributes: [
            NSAttributedString.Key.kern: 10
        ])
        tmpView.onCommitHandler = onCommitHandler
        tmpView.textFieldChangedHandler = changeHandler
        return tmpView
    }

    func updateUIView(_ uiView: WrappableTextField, context: UIViewRepresentableContext<SATextField>) {
        uiView.setContentHuggingPriority(.defaultHigh, for: .vertical)
        uiView.setContentHuggingPriority(.defaultLow, for: .horizontal)
    }
}

Мы устанавливаем атрибутивный текст для заполнителя в makeUIView. Текст заполнителя не обновляется, поэтому нам не нужно беспокоиться об этом.

А вот как мы это используем:

struct ContentView: View {

    @State var text = ""

    var body: some View {
        SATextField(tag: 0, placeholder: "Placeholder", changeHandler: { (newText) in
            self.text = newText
        }) {
            // do something when the editing of this textfield ends
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...