Add bottom border line to UI TextField view in SwiftUI / Swift / Objective-C / Xamarin
Solution 1
I am creating custom textField
to make it reusable component for SwiftUI
SwiftUI
struct CustomTextField: View {
var placeHolder: String
@Binding var value: String
var lineColor: Color
var width: CGFloat
var body: some View {
VStack {
TextField(self.placeHolder, text: $value)
.padding()
.font(.title)
Rectangle().frame(height: self.width)
.padding(.horizontal, 20).foregroundColor(self.lineColor)
}
}
}
Usage:
@Binding var userName: String
@Binding var password: String
var body: some View {
VStack(alignment: .center) {
CustomTextField(placeHolder: "Username", value: $userName, lineColor: .white, width: 2)
CustomTextField(placeHolder: "Password", value: $password, lineColor: .white, width: 2)
}
}
Swift 5.0
I am using Visual Formatting Language (VFL) here, This will allow adding a line to any UIControl
.
You can create a UIView
extension class like UIView+Extention.swift
import UIKit
enum LinePosition {
case top
case bottom
}
extension UIView {
func addLine(position: LinePosition, color: UIColor, width: Double) {
let lineView = UIView()
lineView.backgroundColor = color
lineView.translatesAutoresizingMaskIntoConstraints = false // This is important!
self.addSubview(lineView)
let metrics = ["width" : NSNumber(value: width)]
let views = ["lineView" : lineView]
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[lineView]|", options:NSLayoutConstraint.FormatOptions(rawValue: 0), metrics:metrics, views:views))
switch position {
case .top:
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[lineView(width)]", options:NSLayoutConstraint.FormatOptions(rawValue: 0), metrics:metrics, views:views))
break
case .bottom:
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[lineView(width)]|", options:NSLayoutConstraint.FormatOptions(rawValue: 0), metrics:metrics, views:views))
break
}
}
}
Usage:
textField.addLine(position: .LINE_POSITION_BOTTOM, color: .darkGray, width: 0.5)
Objective C:
You can add this helper method to your global helper class(I used global class method) or in the same view controller(using an instance method).
typedef enum : NSUInteger {
LINE_POSITION_TOP,
LINE_POSITION_BOTTOM
} LINE_POSITION;
- (void) addLine:(UIView *)view atPosition:(LINE_POSITION)position withColor:(UIColor *)color lineWitdh:(CGFloat)width {
// Add line
UIView *lineView = [[UIView alloc] init];
[lineView setBackgroundColor:color];
[lineView setTranslatesAutoresizingMaskIntoConstraints:NO];
[view addSubview:lineView];
NSDictionary *metrics = @{@"width" : [NSNumber numberWithFloat:width]};
NSDictionary *views = @{@"lineView" : lineView};
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[lineView]|" options: 0 metrics:metrics views:views]];
switch (position) {
case LINE_POSITION_TOP:
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[lineView(width)]" options: 0 metrics:metrics views:views]];
break;
case LINE_POSITION_BOTTOM:
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[lineView(width)]|" options: 0 metrics:metrics views:views]];
break;
default: break;
}
}
Usage:
[self addLine:self.textField atPosition:LINE_POSITION_TOP withColor:[UIColor darkGrayColor] lineWitdh:0.5];
Xamarin code:
var border = new CALayer();
nfloat width = 2;
border.BorderColor = UIColor.Black.CGColor;
border.Frame = new CoreGraphics.CGRect(0, textField.Frame.Size.Height - width, textField.Frame.Size.Width, textField.Frame.Size.Height);
border.BorderWidth = width;
textField.Layer.AddSublayer(border);
textField.Layer.MasksToBounds = true;
Solution 2
If you want to do without knowing frames beforehand, without subclassing and without Autolayout:
Swift 5 / Swift 4.x / Swift 3.x
extension UITextField {
func setBottomBorder() {
self.borderStyle = .none
self.layer.backgroundColor = UIColor.white.cgColor
self.layer.masksToBounds = false
self.layer.shadowColor = UIColor.gray.cgColor
self.layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
self.layer.shadowOpacity = 1.0
self.layer.shadowRadius = 0.0
}
}
Call as yourTextField.setBottomBorder()
from anywhere without making sure of the frames to be right.
The Result looks like this:
Swift UI
struct MyTextField: View {
var myPlaceHolder: String
@Binding var text: String
var underColor: Color
var height: CGFloat
var body: some View {
VStack {
TextField(self.myPlaceHolder, text: $text)
.padding()
.font(.title)
Rectangle().frame(height: self.height)
.padding(.horizontal, 24).foregroundColor(self.underColor)
}
}
}
Solution 3
You can create a subclass of UITextField
as shown below:
class TextField : UITextField {
override var tintColor: UIColor! {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
let startingPoint = CGPoint(x: rect.minX, y: rect.maxY)
let endingPoint = CGPoint(x: rect.maxX, y: rect.maxY)
let path = UIBezierPath()
path.move(to: startingPoint)
path.addLine(to: endingPoint)
path.lineWidth = 2.0
tintColor.setStroke()
path.stroke()
}
}
Solution 4
None of these solutions really met my expectations. I wanted to subclass the TextField since I don't want to set the border manually all the time. I also wanted to change the border color e.g. for an error. So here's my solution with Anchors
:
class CustomTextField: UITextField {
var bottomBorder = UIView()
override func awakeFromNib() {
// Setup Bottom-Border
self.translatesAutoresizingMaskIntoConstraints = false
bottomBorder = UIView.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
bottomBorder.backgroundColor = UIColor(rgb: 0xE2DCD1) // Set Border-Color
bottomBorder.translatesAutoresizingMaskIntoConstraints = false
addSubview(bottomBorder)
bottomBorder.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
bottomBorder.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
bottomBorder.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
bottomBorder.heightAnchor.constraint(equalToConstant: 1).isActive = true // Set Border-Strength
}
}
---- Optional ----
To change the color add sth like this to the CustomTextField Class
:
@IBInspectable var hasError: Bool = false {
didSet {
if (hasError) {
bottomBorder.backgroundColor = UIColor.red
} else {
bottomBorder.backgroundColor = UIColor(rgb: 0xE2DCD1)
}
}
}
And to trigger the Error call this after you created an instance of CustomTextField
textField.hasError = !textField.hasError
Hope it helps someone ;)
Solution 5
extension UITextField {
func setBottomBorder(color:String) {
self.borderStyle = UITextBorderStyle.None
let border = CALayer()
let width = CGFloat(1.0)
border.borderColor = UIColor(hexString: color)!.cgColor
border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: self.frame.size.height)
border.borderWidth = width
self.layer.addSublayer(border)
self.layer.masksToBounds = true
}
}
and then just do this:
yourTextField.setBottomBorder(color: "#3EFE46")
Related videos on Youtube
![dhaval shah](https://i.stack.imgur.com/eZDax.jpg?s=256&g=1)
dhaval shah
Updated on August 30, 2021Comments
-
dhaval shah almost 3 years
I would like to keep the border at the bottom part only in
UITextField
. But I don't know how we can keep it on the bottom side.Can you please advise me?
-
dhaval shah over 9 yearsi ll have a look at this one saurabh.But it ll be great of we can make it by code?Do u have any idea?
-
iAkshay over 7 yearsI was thinking to do like this, but if we use this in
viewDidLoad()
, the frame would be incorrect. So we have 2 choices :viewDidLayoutSubviews()
orviewDidAppear()
. ButviewDidLayoutSubviews()
called multiple times and calling fromviewDidAppear()
would not be good experience. -
theDC over 7 yearsDoes it really work? I'm not getting any bottom line
-
saurabh over 7 yearsIt works for me, can you show some of your code, so we can look into
-
theDC over 7 yearsI call it inside
awakeFromNib()
method and it does not return any bottom line. Same for calling it inlayoutSubviews()
-
saurabh over 7 yearsTry in
viewDidLoad()
? -
theDC over 7 yearsI would need to set outlets for my textfields, I'd prefer to do this inside textField class
-
saurabh over 7 years@DCDC: I ran into this problem while using background Color for textField, so I removed the background color and it worked.
-
Kunal Kumar over 7 years
viewDidLayoutSubviews()
will also won't work if the textfield is nested insidemultiple View
. You'll get multiple broder. -
Deepak Chaudhary over 7 yearsBest approach to do. I just wanted to know how to change underline color while editing or during the method "didBeginEditing" and change color on "didEndEditing"
-
user1046037 over 7 yearsSee the updated answer, set the
tintColor
indidBeginEditing
anddidEndEditing
-
markhorrocks about 7 yearsThis didn't work for me, I set it in
viewDidLoad()
-
markhorrocks about 7 yearsThis is the solution I used. I subtracted 4 from maxY to move the underline closer to the text entry.
-
saurabh about 7 years@markhorrocks Can you share your result? I tried it and it worked for me.
-
Satyam about 7 yearsIf we change the background color to clear color, its not working.
-
saurabh about 7 years@Satyam Background color of
UITextField
? -
Satyam about 7 yearsyes. If we change white color to clear color in self.layer.backgroundColor = UIColor.white.cgColor its not working
-
C0D3 almost 7 yearsI think in Swift 4.0 you need to change the "set" to "didSet" but otherwise it works, thank you
-
cheznead almost 7 yearsThis only worked for me if you call it in viewDidLayoutSubviews() and not viewDidLoad(). Was that the intention here?
-
Bruno Ferreira over 6 yearsBest solution so far, you can even modify it for other "validation" states
-
Serdnad almost 6 yearsGreat solution, works with a clear background unlike some of the others.
-
GordonW over 5 yearsremember to add func call to viewDidLayoutSubviews() if using auto layout :) otherwise your line will not correctly match frame.
-
timetraveler90 over 4 yearsIs there an easy way to remove this line after setting it? For example, I want to have it while my textField is active else I'd revert it to the default style.
-
Ilesh P over 4 yearsyeah, simply hold object any remove or hide/show when you need it. :)
-
Abhi Kapoor over 4 yearsWork for me in swift 4.2
-
zach wilcox over 3 yearsCan you make this work with just a simple textView?