Reputation: 12272
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.
seen 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.)
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.
Upvotes: 0
Views: 275
Reputation: 12272
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:
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. (Do note though that if UIKit changes any quality of it thickness, color, whatever, it during the running of an app {perhaps for an animation, state change or whatever reason} you won't know about that and the look will change.)
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 :/ )
Upvotes: 0
Reputation: 77672
Set the text field's .borderStyle
to .roundedRect
, then set the desired border properties on the text field's .layer
itself:
class RedTextFieldVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let tf = UITextField()
tf.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tf)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
tf.widthAnchor.constraint(equalToConstant: 240.0),
tf.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
tf.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
tf.borderStyle = .roundedRect
tf.layer.borderColor = UIColor.red.cgColor
tf.layer.borderWidth = 1.0 / UIScreen.main.scale
tf.layer.cornerRadius = 4.0
tf.layer.cornerCurve = .circular
tf.text = "Hello Red Border"
}
}
Result:
Edit
Maybe give this a try?
class MyBorderedTextField: UITextField {
override class var layerClass: AnyClass { CAShapeLayer.self }
private var shapeLayer: CAShapeLayer { layer as! CAShapeLayer }
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = 1.0 / UIScreen.main.scale
}
override func layoutSubviews() {
super.layoutSubviews()
// tweak cornerRadius to suit
shapeLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 6.0).cgPath
}
override func textRect(forBounds bounds: CGRect) -> CGRect {
// tweak the 7,6 insets to suit
return bounds.insetBy(dx: 7.0, dy: 6.0)
}
}
class RedTextFieldVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let tf = UITextField()
tf.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tf)
let tf2 = MyBorderedTextField()
tf2.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tf2)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
tf.widthAnchor.constraint(equalToConstant: 240.0),
tf.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
tf.centerXAnchor.constraint(equalTo: g.centerXAnchor),
tf2.widthAnchor.constraint(equalToConstant: 240.0),
tf2.topAnchor.constraint(equalTo: tf.bottomAnchor, constant: 8.0),
tf2.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
tf.borderStyle = .roundedRect
tf.text = "Hello Plain Border"
tf2.text = "Hello Red Border"
}
}
Output:
Upvotes: 1