Infinite loop in UITextField subclass when overriding layoutSubviews

Throughout our app we generally override layoutSubviews for setting text/background colours on UI elements to support dark mode switching. We have encountered an issue with a custom UITextField class (subclass) we use for all our text fields throughout the app.


We override layoutSubviews and set five background colours within it. No changes are made to any constraints on any views. This works as expected on iOS 13 and above. However when testing on iOS 11 and 12 simulators, after typing a couple of letters in a text field, layoutSubviews ends up being called over and over in an infinite loop and freezing the app.


We have worked around this issue (although it was a pain to debug as it wasn't initally clear it was a text field issue) but it would be interesting to know if anyone else has had similiar issues and some sort of explanation why this might happen. It definitely seems like a bug.

Could you show code for UITextField subclass ? Including viewLayout override of course.

We override

layoutSubviews
and set five background colours within it.

How are you setting these background colours? By modifying the text attributed string, for example, by setting

attributedText
property?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

No attributes strings in this case. I've added the code below for the complete subclass.


This code doesn't crash at all on iOS 13.

It crashes *sometimes* on iOS 11 and 12 simulators with layoutSubviews in an infinite loop. Note it doesn't crash immediately or on first load. Sometimes it will take a couple of clicks within the text box or some letters being typed.


import UIKit


class FormTextField: UITextField {


    // MARK: Layout overrides
    override func textRect(forBounds bounds: CGRect) -> CGRect {
        return CGRect(x: bounds.origin.x + 10, y: bounds.origin.y, width: bounds.width + 10, height: bounds.height)
    }
    
    override func editingRect(forBounds bounds: CGRect) -> CGRect {
        return CGRect(x: bounds.origin.x + 10, y: bounds.origin.y, width: bounds.width + 10, height: bounds.height)
    }
    
    // MARK: Private properties
    private var hintText: String?
    
    // MARK: Life Cycle
    init(hintText: String? = nil) {
        super.init(frame: .zero)
        
        self.autocapitalizationType = .none
        self.autocorrectionType = .no
        self.spellCheckingType = .no
        self.font = UIFont.font(type: .helveticaNeueRegular, size: 15)
        self.layer.borderColor = UIColor.mswGrayLight.cgColor
        self.layer.borderWidth = 0.5
        self.layer.cornerRadius = 2
        self.hintText = hintText
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        styleViews()
    }
    
    // MARK: Theme Support
    private func styleViews() {
        self.layer.borderColor = UIColor.textAreaOutline.cgColor
        self.tintColor = .mswLightBlue
        self.textColor = .textFieldText
    }
}

Why do you call styleViews() on layout and not only at init ?


Could you try the change ?


Could you see the pattern of infinite loop ?

Does it involve textRect and / or editingRect ?

Is it called in layoutSubviews to support dark mode. layoutSubviews is called on all views when the system dark mode setting is turned on or off. The colour specified have a light and dark variety.


Removing the textRect and editingRect does not stop the issue. If I remove the line

self.textColor = .textFieldText 


The crash doesn not occur, so it seems this line is causing an issue.


I recreate it by having two text fields and;

- focus on first text field and type a letter

- focus on second text field and type a letter

- attempt to focus on the first text field - at this point layoutSubviews goes into an infinite loop.

Infinite loop in UITextField subclass when overriding layoutSubviews
 
 
Q