UITextField,文本在编辑开始时会略微跳转

时间:2017-03-29 05:44:56

标签: ios swift uitextfield

我这里有一个奇怪的问题。我的表格单元格中有UITextFields。当我选择字段时,文本会略微向下跳跃:

enter image description here

字体是系统默认值17.我已将adjust to fit打开,大小为17.我已尝试关闭adjust to fit但仍有跳转。我尝试过使用不同的边框样式,这也没什么区别。我试过关闭剪辑到界限,它仍然跳跃。我也试过让框架更高(更高),它仍然跳跃。唯一有效的是如果我将字体大小缩小,例如13.我在这里做错了什么?如果我可以使字体更小以修复跳转,那么为什么不使框架更大?对此的任何指示都将非常感激。谢谢!

4 个答案:

答案 0 :(得分:11)

我必须承认,在我的开发过程中,我总是看到这个小小的反弹,但我从不调查它。 我用来复制你的问题的一些要点:

  1. 两个文本字段,默认维度和设置
  2. 使用系统默认字体并将其大小从13更改为 测试行为
  3. 我想还有更多其他方法可以解决你的问题但是我没有找到一个快速的属性来阻止这个文本的“跳跃”。我决定分析当前的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输出

    enter image description here

    基本上似乎有 3 CALayer

    • 一个框架= CGRect (0 0; 252 30)的图层 _UITextFieldRoundedRectBackgroundViewNeue作为与我们的文本字段具有相同维度的委托
    • 一个框架= CGRect (7 2; 238 26)的图层,仅在我们的图片中显示 textFieldDidBeginEditing并且UIFieldEditor为代理人 这似乎是编辑部分的责任..
    • 仅在textFieldDidEndEditing中显示为frame = CGRect.zero并且具有委托UITextFieldLabel
    • 的图层

    如果我向第一个textField写了一些内容然后我转到下一个文本字段没有发生但是如果我返回到第一个textField然后到第二个那个帧等于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:

    enter image description here

    正如我们所看到的那样,我们在该层中始终存在 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
                }
            }
        }
    }
    

    我已经测试了这个新的扩展程序,它的行为与旧的扩展程序一样。

答案 1 :(得分:3)

所以你可能偶然发现了一些非常奇怪的事情。如果你将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)

在样本中我正在使用暴力迫使y位置确实有帮助,但并不是真的推荐。我怀疑这个问题与看似随机的100 x 100的界限有关。希望这里有些东西可以帮助你找到适合你的解决方案。

答案 2 :(得分:2)

更改UITextField的contentVerticalAlignment属性可能导致这种奇怪的行为。

您可以添加符号断点

  

[UITextField setContentVerticalAlignment:]

检查是否更改了contentVerticalAlignment属性 UITextField意外。

答案 3 :(得分:-2)

您可以尝试在UITextField中增加inputView的高度。

UITextField具有inputView,它将在对象成为第一响应者时显示。

相关问题