是否可以在 SwiftUI 中为 TextField 添加字距调整?
Posted
技术标签:
【中文标题】是否可以在 SwiftUI 中为 TextField 添加字距调整?【英文标题】:Is it possible to add kerning to a TextField in SwiftUI? 【发布时间】:2019-11-05 15:13:49 【问题描述】:要匹配样式指南,我必须向文本字段添加字距调整,包括占位符和值本身。
借助 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 的值或占位符的方法。关于这个有什么想法吗?提前致谢
【问题讨论】:
如果您需要 UIKit 视图,请使用 UIViewRepresentable。 @matt 我已经在 SwiftUI 中实现了视图,并希望坚持下去。在 SwiftUI 视图inside 中只使用唯一的 UITextField 子类是不可能的,是吗? 是的;这正是你可以做的,使用 UIViewRepresentable。 SwiftUI 视图可以存在于 UIViews 中,而 UIViews 可以存在于 SwiftUI 视图中。 【参考方案1】: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
])
更新
以上方法不适用于在属性文本上设置字距。借鉴 Costantino Pistagna 在他的medium article 中所做的出色工作,我们需要做更多的工作。
首先,我们需要创建一个 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
,它会按预期工作
现在我们可以在UIViewRepresentable
中使用WrappedTextField
。
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
【讨论】:
嗯……实际上,它工作得很好,但我无法让内部填充工作……This answer 过去对我来说工作得很好:( :) 我很高兴字距调整现在为您工作。如果您有一个与字距调整无关的问题,那么我建议您将其写成一个新问题,以便您可以清楚地表达您需要做的所有事情,因为 SO 上的 cmets 并不允许进行详细的讨论。一方面,这里的代码格式不好;其次,你被限制在 600 个字符以内,所以很难充分表达自己。以上是关于是否可以在 SwiftUI 中为 TextField 添加字距调整?的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI 如何获取对 List 内编辑 Textfield 的参考?
如何在 SwiftUI 中为 Start->Max->Start 设置动画?