Add bottom border line to UI TextField view in SwiftUI / Swift / Objective-C / Xamarin Add bottom border line to UI TextField view in SwiftUI / Swift / Objective-C / Xamarin ios ios

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:

sample

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()    }}