我必须承认,我在开发中一直看到这个小弹跳,但从未对它进行过调查。以下是我用来复制您的问题的一些要点:
我想可能有更多其他方法来解决您的问题,但我没有找到一个快速的属性来停止这个小的文字“弹跳”。 我决定分析当前的UITextField
($ xcrun swift -version = Swift 3.1)以查看其组成:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var myTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
myTextField.delegate = self
}
func textFieldDidBeginEditing(_ textField: UITextField) {
print("\ntextFieldDidBeginEditing")
showLayersDescription()
}
func textFieldDidEndEditing(_ textField: UITextField) {
print("\ntextFieldDidEndEditing")
showLayersDescription()
}
func showLayersDescription() {
myTextField.layer.sublayers?.forEach{ print($0.debugDescription)}
}
}
其中myTextField
实际上是两个文本字段中的一个。
使用字体大小17的输出:
主要似乎有3个CALayer
:
CGRect (0 0; 252 30)
的图层,它的代理是_UITextFieldRoundedRectBackgroundViewNeue
,它的尺寸与我们的文本字段相同CGRect (7 2; 238 26)
的图层,仅在我们的textFieldDidBeginEditing
中显示,其代理为UIFieldEditor
,看起来负责编辑部分..textFieldDidEndEditing
中出现,帧为CGRect.zero
,并带有代理UITextFieldLabel
如果我向第一个文本字段写入内容,然后转到下一个文本字段,则不会发生任何事情。但是,如果我返回第一个文本字段,然后转到第二个文本字段,具有和CGRect.zero
相等尺寸的图层会更改为CGRect(7 0.5; 238 27.5)
这是我们在编辑开始和结束时看到的小高度(0.5)。
我们可以尝试使用扩展来优化调试并拦截这些图层:
extension UITextField
{
func debugLayers() {
print("We have the follow layers:")
let FieldEditor: AnyObject.Type = NSClassFromString("UIFieldEditor")!
let LabelLayer: AnyObject.Type = NSClassFromString("_UILabelLayer")!
let TextFieldRoundRect: AnyObject.Type = NSClassFromString("_UITextFieldRoundedRectBackgroundViewNeue")!
self.layer.sublayers?.forEach{
if ($0.delegate?.self.isKind(of: TextFieldRoundRect))! { print("- layer with _UITextFieldRoundedRectBackgroundViewNeue as delegate have frame:\($0.frame)") }
if ($0.delegate?.self.isKind(of: FieldEditor))! { print("- layer with UIFieldEditor as delegate have frame:\($0.frame)") }
if $0.self.isKind(of: LabelLayer) { print("- layer is kind of _UILabelLayer have frame:\($0.frame)") }
}
}
}
所以我们举个例子:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var myTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
myTextField.delegate = self
}
func textFieldDidBeginEditing(_ textField: UITextField) {
print("\ntextFieldDidBeginEditing")
myTextField.debugLayers()
}
func textFieldDidEndEditing(_ textField: UITextField) {
print("\ntextFieldDidEndEditing")
myTextField.debugLayers()
}
}
始终使用17号字体输出:
正如我们所看到的,在该层中存在0.5个高度差异。
通过其他尝试,我发现这种行为仅在默认大小介于13和17之间时发生,在13以下和18到25之间则没有,正如您在问题中所述。
我认为截取并尝试纠正这种情况的最佳方法是创建一个新的扩展程序:
extension UITextField
{
override open func layoutSubviews() {
super.layoutSubviews()
let FieldEditor: AnyObject.Type = NSClassFromString("UIFieldEditor")!
let LabelLayer: AnyObject.Type = NSClassFromString("_UILabelLayer")!
self.layer.sublayers?.forEach{
if ($0.delegate?.self.isKind(of: FieldEditor))! {
var f = $0.frame
f.origin.y = 0.0
$0.frame = f
}
if $0.self.isKind(of: LabelLayer) {
var layerFrame = CGRect.zero
layerFrame.origin = self.editingRect(forBounds: self.bounds).origin
layerFrame.size = self.editingRect(forBounds: self.bounds).size
if let size = self.font?.pointSize, 14 ... 17 ~= size {
layerFrame.origin.y = -0.5
} else {
layerFrame.origin.y = 0.0
}
$0.frame = layerFrame
}
}
}
}
此扩展可防止在切换到另一个textField时文本出现小的“跳动”。这可能是为了平衡:
contentsCenter = CGRect (0.485 0.485; 0.000588235 0.000588235)
第二和第三层与该字体大小组相关,如您在扩展中所见,我将带有 UIFieldEditor
代理的图层高度原点设置为零(之前高度为2.0),以平衡约束差异。
我已阅读您的评论,因此我深入分析了图层的情况:我发现高度差为-0.5的图层从未呈现子图层,而另一个则总是呈现一个子图层(其代理为UIFieldEditor
),因此我们可以轻松地进行扩展修正:
extension UITextField
{
override open func layoutSubviews() {
super.layoutSubviews()
self.layer.sublayers?.forEach{
if let subs = $0.sublayers, subs.count>0 {
var f = $0.frame
f.origin.y = 0.0
$0.frame = f
} else {
var layerFrame = CGRect.zero
layerFrame.origin = self.editingRect(forBounds: self.bounds).origin
layerFrame.size = self.editingRect(forBounds: self.bounds).size
if let size = self.font?.pointSize, 14 ... 17 ~= size {
layerFrame.origin.y = -0.5
} else {
layerFrame.origin.y = 0.0
}
$0.frame = layerFrame
}
}
}
}
我已经测试了这个新扩展程序,它的行为与旧的扩展程序一样正确。
所以您可能会遇到一些非常奇怪的问题。如果您将UITextField子类化为以下内容。
class NonJumpyTextField: UITextField {
override func textRect(forBounds bounds: CGRect) -> CGRect {
var previousRect = super.textRect(forBounds: bounds)
print("Bounds: \(bounds)")
print("TextRect: \(previousRect)")
print("TextFieldRect \(frame)")
//not reccomended but does seem to help
previousRect.origin.y = 2.25
return previousRect
}
}
当编辑时,读取日志会显示一些奇怪的行为。
Bounds: (0.0, 0.0, 100.0, 100.0)
TextRect: (7.0, 2.0, 86.0, 96.0)
TextFieldRect (64.0, 66.0, 97.0, 30.0)
Bounds: (0.0, 0.0, 97.0, 30.0)
TextRect: (7.0, 2.0, 83.0, 26.0)
TextFieldRect (64.0, 66.0, 97.0, 30.0)
Bounds: (0.0, 0.0, 100.0, 100.0)
TextRect: (7.0, 2.0, 86.0, 96.0)
TextFieldRect (64.0, 66.0, 97.0, 30.0)
编辑完成后
Bounds: (0.0, 0.0, 97.0, 30.0)
TextRect: (7.0, 2.0, 83.0, 26.0)
TextFieldRect (64.0, 66.0, 97.0, 30.0)
Bounds: (0.0, 0.0, 100.0, 100.0)
TextRect: (7.0, 2.0, 86.0, 96.0)
TextFieldRect (64.0, 66.0, 97.0, 30.0)
改变UITextField的contentVerticalAlignment
属性可能会导致奇怪的行为。
您可以添加一个符号断点
[UITextField setContentVerticalAlignment:]
来检查是否意外更改了UITextField的contentVerticalAlignment
属性。