I'm having an odd issue here. I have UITextFields in my table cells. When I select the field the text jumps very slightly down:
The font is system default 17. I have adjust to fit
turned on at size 17. I have tried turning off adjust to fit
and there is still a jump. I have tried using different border styles and this also makes no difference. I have tried turning off clip to bounds, it still jumps. I have also tried making the frame taller (much taller) and it still jumps. The only thing that works is if I make the font size much smaller eg 13. What am I doing wrong here? If I can make the font smaller to fix the jump then why doesn't making the frame bigger work? Any pointers on this would be really appreciated. Thanks!
I must admit that I've always seen this little bouncing during my developments but I never investigate around it. Some points that I've used to replicate your issue:
I suppose there are more other ways to solve your problem but I don't find a fast property to stop this little "jumping" of text. I've decide to analize the current UITextField
($ xcrun swift -version = Swift 3.1) to see it's composition:
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)}
}
}
where myTextField
is essentially one of the two textfields
Output using font size 17:
Essentially seems there are 3 CALayer
:
CGRect (0 0; 252 30)
that have
_UITextFieldRoundedRectBackgroundViewNeue
as delegate that have the same dimension of our textfieldCGRect (7 2; 238 26)
that is showed only in our
textFieldDidBeginEditing
and it have UIFieldEditor
as delegate
that seems to be the responsible for the editing part..textFieldDidEndEditing
with frame = CGRect.zero
and with delegate UITextFieldLabel
If I write something to the first textField then I go to the next textfield nothing happened BUT if I return to the first textField and then to the second the layer with frame equal to CGRect.zero
change to frame = CGRect(7 0.5; 238 27.5)
This is the little height (0.5) that we can see during the beginning and the ending of our editing.
We can try to refine the debug and intercept these layers with an extension:
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)") }
}
}
}
So we have for example:
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()
}
}
Output always with font size 17:
As we can see we have always this 0.5 difference in height in that layer..
Making other tries I've seen that this behaviour happened only if the default size is between 13 and 17, under 13 and from 18 to 25 this not happened has you've report to your question.
I think the best way to intercept and trying to correct this one it's to make a new extension:
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
}
}
}
}
This extension suppress the little "jumping down" of the text during the change to another textField. This is probably to balance the :
contentsCenter = CGRect (0.485 0.485; 0.000588235 0.000588235)
that both the second and third layer have with this group of font sizes.
As you see in the extension I've set to zero also the height origin of the layer with UIFieldEditor
delegate (that before have 2.0 as height) because it's involved to this change, I've maded it to balance the constraints differences.
I've read your comment so I've analyzed in deep the situation about layers: what I've found is that the layer who have the gap of -0.5 height NEVER present sublayers, when the other ALWAYS present one sublayer ( have UIFieldEditor
as delegate) so we can easily correct the extension as:
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
}
}
}
}
I've tested this new extension and it behaves correctly like the old one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With