Using a custom font in a UITextField causes it to shift slightly when accessed — is there a fix?

前端 未结 10 1962
后悔当初
后悔当初 2020-12-13 14:32

I have a custom font in a UITextField, and I\'ve noticed that when it\'s accessed (when the keyboard appears), the text shifts down by a very small amount -- maybe

相关标签:
10条回答
  • 2020-12-13 14:43

    This is expected behaviour in a standard UITextField. You can however solve this by subclassing UITextField and by adjusting the bounds for the text itself.

    Swift 3

    override func textRect(forBounds bounds: CGRect) -> CGRect {
        return(bounds.insetBy(dx: 0, dy: 0))
    }
    
    override func editingRect(forBounds bounds: CGRect) -> CGRect {
        return(bounds.insetBy(dx: 0, dy: -0.5))
    }
    
    override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
        return(bounds.insetBy(dx: 0, dy: 0))
    }
    

    This should do the trick!

    0 讨论(0)
  • 2020-12-13 14:46

    Swift 3

    Do not forget the accessory views of the UITextField. You'll need to account for super of the *rect(forBounds: ...) functions if you want a working implementation. And be also sure to only displace the rects for the buggy iOS 10 and not for 9 or 8! The following code should do the trick:

    public class CustomTextField: UITextField {
        public override func textRect(forBounds bounds: CGRect) -> CGRect {
            let superValue = super.textRect(forBounds: bounds)
    
            if #available(iOS 10, *) {
                return superValue.insetBy(dx: 0, dy: 0)
            }
            return superValue
        }
    
        public override func editingRect(forBounds bounds: CGRect) -> CGRect {
            let superValue = super.editingRect(forBounds: bounds)
    
            if #available(iOS 10, *) {
                return superValue.insetBy(dx: 0, dy: -0.5)
            }
            return superValue
        }
    
        public override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
            let superValue = super.placeholderRect(forBounds: bounds)
    
            if #available(iOS 10, *) {
                if isEditing {
                    return superValue.insetBy(dx: 0, dy: 0.5)
                }
                return superValue.insetBy(dx: 0, dy: 0.0)
            }
            return superValue
        }
    }
    

    EDIT

    I slightly edited my code from above to the following and it works better for me. I testet it on iPhone 6, 6s, 7, 7s as well as the 'plus' devices with iOS 9.3 and 10.3.

    public class CustomTextField: UITextField {
    
        public override func textRect(forBounds bounds: CGRect) -> CGRect {
            let superValue = super.textRect(forBounds: bounds)
    
            if #available(iOS 10, *) {
                return superValue.insetBy(dx: 0, dy: -0.3)
            }
            return superValue.insetBy(dx: 0, dy: -0.2)
        }
    
        public override func editingRect(forBounds bounds: CGRect) -> CGRect {
            return self.textRect(forBounds: bounds)
        }
    }
    

    I think it also depends on the font you use. I use UIFont.systemFont(ofSize: 17.0, weight: UIFontWeightLight)

    0 讨论(0)
  • 2020-12-13 14:48

    For a strange reason I didn't really understood I've solved this by setting automaticallyAdjustsScrollViewInsets to NO (or equivalent in Interface Builder). This with iOS 8.1.

    0 讨论(0)
  • 2020-12-13 14:50

    Unfortunately none of the answers worked for me.

    @blackjacx answer worked but only sometimes :(

    I started out debugging and here is what I've discovered:

    1 - The real problem seems to be with a private subview of UITextField of type UIFieldEditorContentView

    Below you can see that the y of it subview is not the same of the UITextField itself:

    After realizing it I came out with the following workaround:

    override func layoutSubviews() {
        super.layoutSubviews()
        fixMisplacedEditorContentView()
    }
    
    func fixMisplacedEditorContentView() {
        if #available(iOS 10, *) {
            for view in subviews {
                if view.bounds.origin.y < 0 {
                    view.bounds.origin = CGPoint(x: view.bounds.origin.x, y: 0)
                }
            }
        }
    }
    

    You will need to subclass UITextField and override layoutSubviews to add the ability to manually set to 0 the y of any subview that is set to a negative value. As this problem doesn't occur with iOS 9 our below I added a check to do the workaround only when it is on iOS 10.

    The result you can see below:

    2 - This workaround doesn't work if the user choose to select a subrange of the text (selectAll works fine)

    Since the selection of the text is not a must have for my app I rather disable it. In order to do that you can use the following code (Swift 3):

    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        if #available(iOS 10, *) {
            if action == #selector(UIResponderStandardEditActions.select(_:)) {
                return false
            }
        }
    
        return super.canPerformAction(action, withSender: sender)
    }
    
    0 讨论(0)
  • 2020-12-13 14:50

    I had this issue with a custom font and solved it by shifting the label in the other direction when the keyboard events would fire. I moved the center of the label in the button by overriding the drawRect: method

    - (void)drawRect:(CGRect)rect
    {
        self.titleLabel.center = CGPointMake(self.titleLabel.center.x, self.titleLabel.center.y+3);
    }
    
    0 讨论(0)
  • 2020-12-13 14:50

    Set your textfields Border Style to any value except "none" in IB, then, in your ViewController's viewDidLoad set:

    yourTextField.borderStyle = .none
    

    (Based on this answer by Box Jeon)

    0 讨论(0)
提交回复
热议问题