swiftuitextfieldcalayerios17

in iOS17 UIKit is there a way to change/affect the border color of an iOS17 UITextField (other than the mangle shown here)?


This is about iOS 17 only. The only way I could hack around and achieve something like this was

so

///True™ iOS look of the border, and you can change the color
class FixedUITextField: UITextField {
    
    var completed: Bool = true {
        didSet {
            fakeLayer.borderColor = completed ? rememberDefaultColor : UIColor.red.cgColor
        }
    }
    
    private var rememberDefaultColor: CGColor? = UIColor.gray.cgColor
    
    private lazy var fakeLayer: CALayer = {
        let v = RepairedCALayer()
        for found in layer.sublayers ?? [] {
            if found.borderWidth > 0 {
                layer.insertSublayer(v, above: found)
                v.backgroundColor = found.backgroundColor
                v.borderColor = found.borderColor
                v.borderWidth = found.borderWidth
                v.cornerRadius = found.cornerRadius
                v.cornerCurve = found.cornerCurve
                print("found and covered")
                rememberDefaultColor = found.borderColor
                return v
            }
        }
        // defaults ICO disaster
        v.backgroundColor = UIColor.systemBackground.cgColor
        v.borderColor = UIColor.blue.cgColor
        v.borderWidth = 1.0 / UIScreen.main.scale
        v.cornerRadius = 4.0
        v.cornerCurve = .circular
        layer.addSublayer(v)
        return v
    }()
    
    override func layoutSubviews() {
        super.layoutSubviews()
        fakeLayer.frame = bounds
    }
}

This is about iOS 17 only. Am I missing something really obvious?

This is about iOS 17 only. Even more issues on this time waste ...

Am I missing something really obvious? - is there a way to change the border color of a UITextField as is?


It looks like if you set the border style on a UITextField these days,

override func common() {
    super.common()
    borderStyle = .roundedRect
    layer.borderWidth = 1.0 / UIScreen.main.scale
    layer.cornerCurve = .circular
    layer.cornerRadius = 10

It just adds that to the standard border.

enter image description here

seen here

enter image description here

(tap for large)

If there was a way to turn off the mystery layer (or draw ?), that would be a work around, but I just cannot find a way to turn it off. (If you hide it, "they" turn it on again after a few frames! I don't know how to examine the source code to see what's going on.)

NB, from storyboard ...

If you're investigating this recall that is it begins from storyboard they set borderStyle = .roundedRect (it's .none if you just instantiate a text field), adding further confusion.


Solution

  • Answer is: "it cannot be done"; here's a workaround

    In iOS17 UIKit is there really ANY way at all to changethe border color of an iOS17 UITextField?

    No.

    You have two and a half possibilities:

    1. Use the mangle I give in the question, which will exactly copy their border and cover it with an exact match, which you can color as wished. (However, if UIKit changes any quality during the running of an app {eg for an animation, state change, etc} you won't know about that and the look will change.)

    2. Very simply eliminate "their" border and use your own. The only trick to doing so is that you have to set the text rect (which is essentially setting the padding visually):


    ///Essentially, you can't CHANGE THE COLOR of the border in a UITextField.
    ///Here, we simply turn it off and recreate it strictly BY EYE.
    ///Don't expect this to match UITextField in any serious project.
    class ManualBorderUIITextField: UIITextField {
        
        override func common() {
            super.common()
            borderStyle = .none
            layer.borderWidth = 2.0 / UIScreen.main.scale
            // In 2024 iOS it's 1 pixel there, I like 2
            layer.cornerCurve = .circular
            layer.cornerRadius = 4
            layer.borderColor = UIColor.green.cgColor
            // Choice is yours. In most real projects you'd be changing it per your use/states etc
        }
        
        ///Note that very confusingly UITextField will do the intrinsic size for you backwards from this, you don't have to.
        override func textRect(forBounds bounds: CGRect) -> CGRect {
            return bounds.insetBy(dx: 7.0, dy: 8.0)
            // When you kill the border style this padding becomes zero so you have to do it manually
            // In 2024 iOS it's possibly ~7/~6 there but hard to say exactly
        }
    }
    

    2.5. I guess if you are incredibly anal you could do "1" when you launch a text field, memorize all the values, and then use that in a "2" approach. (Again though, you wouldn't know about any changes "they" might make in theory during the run; you're very simply building a custom control like any other custom control :/ )