Add bottom border line to UI TextField view in SwiftUI / Swift / Objective-C / Xamarin
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: Stringvar 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 UIKitenum 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;
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) } }}
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() }}