是否可以在 SwiftUI 中向 TextField 添加字距调整? [英] Is it possible to add kerning to a TextField in SwiftUI?

查看:32
本文介绍了是否可以在 SwiftUI 中向 TextField 添加字距调整?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

要匹配样式指南,我必须向文本字段添加字距调整,包括占位符和值本身.

To match a Styleguide I have to add kerning to a textfield, both placeholder and value itself.

使用 UIKit 我能够做到这一点:

With UIKit I was able to do so with:

    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 元素应用字距调整效果:

In SwiftUI, I could figure out that I could apply a kerning effect to a Text element like so:

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

不幸的是,我找不到将字距调整样式应用于 TextField 的值和占位符的方法.关于这个有什么想法吗?提前致谢

Unfortunately, I could not find a way to apply a kerning style to neither a TextField's value nor placeholder. Any ideas on this one? Thanks in advance

推荐答案

有一个关于 HackingwithSwift 展示了如何实现 UITextView.它可以很容易地适应 UITextField.

There is a simple tutorial on HackingwithSwift that shows how to implement a UITextView. It can easily be adapted for UITextField.

这是一个快速示例,展示了如何为您使用 UIViewRepresentable UITextField.在文本和占位符上设置字距调整.

Here is a quick example showing how to use UIViewRepresentable for you UITextField. Setting the kerning on both the text and the placeholder.

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
        ])
    }
}

<小时>

更新

以上不适用于在 attributedText 上设置字距调整.借鉴 Costantino Pistagna 在他的中篇文章中所做的出色工作,我们需要多做一点工作.


Update

The above doesn't work for setting the kerning on the attributedText. Borrowing from the fantastic work done by Costantino Pistagna in his medium article we need to do a little more work.

首先,我们需要创建一个封装的 UITextField 版本,允许我们访问委托方法.

Firstly we need to create a wrapped version of the UITextField that allows us access to the delegate methods.

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

As the shouldChangeCharactersIn delegate method will get called every time the text changes, we should use that to update the attributedText value. I tried using first the proposedVale but it would double up the characters, it works as expected if we use the currentValue

现在我们可以在 UIViewRepresentable 中使用 WrappedTextField.

Now we can use the WrappedTextField in the 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 中为占位符设置了属性文本.占位符文本没有被更新,所以我们不需要担心改变它.

We set the attributed text for the placeholder in the makeUIView. The placeholder text is not being updated so we don't need to worry about changing that.

这是我们如何使用它:

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
        }
    }
}

这篇关于是否可以在 SwiftUI 中向 TextField 添加字距调整?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆