I created a little project (Github link) demonstrating how to add a line under a UITextField
to answer this SO question.
The OP was trying to add the underline using a CALayer, so that is how I originally implemented the solution. For that version, I created a custom subclass of UITextField
called "UnderlinedTextField". That custom subclass creates and maintains a CALayer with a background color.
I then noted that it would be easier to simply add a 1-point UIView
at the bottom of the text field. I updated my demo project to illustrate that approach as well.
Both approaches add a 1-point blue line under the UITextField
. The view-based approach does everything in the Storyboard, and there is no code needed. Since it uses AutoLayout constraints to pin the underline view to the bottom of the text field, the constraints keep the view at the right location even if the text field moves.
However, I noticed that the layer-based underline and the view-based underline look different here is a screenshot:
The layer-based underline looks heavier. In looking at it, the color used for anti-aliasing on the layer-based underline is darker than the color used in anti-aliasing the underline for the view-based underline.
Why is that, and what would I change to make them look the same?
(Below is the code for the UnderlinedTextField
class, in case you don't want to go look at the Github repo)
/// This is a custom subclass of UITextField that adds a 1-point colored underline under the text field using a CALayer.
/// It implments the `layoutSubviews()` method to reposition the underline layer if the text field is moved or resized.
class UnderlinedTextField: UITextField {
/// Change this color to change the color used for the underline
public var underlineColor = UIColor.blue {
didSet {
underlineLayer.backgroundColor = underlineColor.cgColor
}
}
private let underlineLayer = CALayer()
/// Size the underline layer and position it as a one point line under the text field.
func setupUnderlineLayer() {
var frame = self.bounds
frame.origin.y = frame.size.height - 1
frame.size.height = 1
underlineLayer.frame = frame
underlineLayer.backgroundColor = underlineColor.cgColor
}
required init?(coder: NSCoder) {
super.init(coder: coder)
// In `init?(coder:)` Add our underlineLayer as a sublayer of the view's main layer
self.layer.addSublayer(underlineLayer)
}
override init(frame: CGRect) {
super.init(frame: frame)
// in `init(frame:)` Add our underlineLayer as a sublayer of the view's main layer
self.layer.addSublayer(underlineLayer)
}
// Any time we are asked to update our subviews,
// adjust the size and placement of the underline layer too
override func layoutSubviews() {
super.layoutSubviews()
setupUnderlineLayer()
}
}
Edit:
I changed my project based on Matt's answer (and pushed the changes) to make the Storyboard use the device color profile. That makes the underlines' color values very close, but still not exactly the same.
If I inspect the lines in the simulator using the "Digital Color meter" app the top and bottom lines are subtly different. However, if I save the screen to disk in the simulator with control S (which saves it at full size) and open the resulting image in PS, the lines show as un-aliased pure sRGB blue. I guess there's some weirdness with mapping the simulator screen to the Mac screen that causes the 2 lines to be aliased slightly differently.
(You'll have to open the picture in PS or another app that lets you view pixel color to detect the difference in the two lines. After changing the storyboard's color profile to device RGB (sRGB) I can't see the difference any more.)