SwiftUI
Swift
Objective-C
Xamarin
TextField Border

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

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

A bottom-only border on a text field is one of the most common design patterns in modern mobile apps, inspired by Material Design's text input style. Unlike a full rectangular border, a single bottom line keeps the interface minimal while still clearly indicating where the user can type. Implementing this varies significantly across SwiftUI, UIKit (Swift), Objective-C, and Xamarin, so this article covers all four approaches with working code examples.

SwiftUI -- Overlay with Rectangle

SwiftUI does not have a built-in bottom border modifier, but you can achieve the effect with an overlay containing a positioned Rectangle:

swift
1struct BottomBorderedTextField: View {
2    @State private var text = ""
3
4    var body: some View {
5        TextField("Enter your name", text: $text)
6            .padding(.vertical, 8)
7            .overlay(
8                Rectangle()
9                    .frame(height: 1)
10                    .foregroundColor(.gray),
11                alignment: .bottom
12            )
13            .padding(.horizontal, 16)
14    }
15}

The Rectangle is constrained to 1 point in height and pinned to the bottom edge via the alignment: .bottom parameter. You can adjust the thickness by changing the frame(height:) value and the color with foregroundColor.

For a reusable modifier, extract the logic into a ViewModifier:

swift
1struct BottomBorder: ViewModifier {
2    var color: Color
3    var thickness: CGFloat
4
5    func body(content: Content) -> some View {
6        content
7            .overlay(
8                Rectangle()
9                    .frame(height: thickness)
10                    .foregroundColor(color),
11                alignment: .bottom
12            )
13    }
14}
15
16extension View {
17    func bottomBorder(color: Color = .gray, thickness: CGFloat = 1) -> some View {
18        modifier(BottomBorder(color: color, thickness: thickness))
19    }
20}

Now any text field can use it:

swift
TextField("Email", text: $email)
    .bottomBorder(color: .blue, thickness: 2)

UIKit Swift -- CALayer Sublayer

In UIKit, you add a CALayer as a sublayer to the text field's layer. The key detail is positioning the layer at the bottom of the field's bounds:

swift
1class BottomBorderTextField: UITextField {
2    private let borderLayer = CALayer()
3
4    var borderColor: UIColor = .gray {
5        didSet { borderLayer.backgroundColor = borderColor.cgColor }
6    }
7
8    var borderThickness: CGFloat = 1.0 {
9        didSet { setNeedsLayout() }
10    }
11
12    override init(frame: CGRect) {
13        super.init(frame: frame)
14        setupBorder()
15    }
16
17    required init?(coder: NSCoder) {
18        super.init(coder: coder)
19        setupBorder()
20    }
21
22    private func setupBorder() {
23        borderStyle = .none
24        borderLayer.backgroundColor = borderColor.cgColor
25        layer.addSublayer(borderLayer)
26    }
27
28    override func layoutSubviews() {
29        super.layoutSubviews()
30        borderLayer.frame = CGRect(
31            x: 0,
32            y: bounds.height - borderThickness,
33            width: bounds.width,
34            height: borderThickness
35        )
36    }
37}

Setting borderStyle = .none removes the default UIKit border so only your custom bottom line appears. The layoutSubviews override ensures the border adjusts when the text field resizes, which is critical for Auto Layout and device rotation.

For a quick one-off without subclassing, use an extension:

swift
1extension UITextField {
2    func addBottomBorder(color: UIColor = .gray, thickness: CGFloat = 1.0) {
3        let border = CALayer()
4        border.backgroundColor = color.cgColor
5        border.frame = CGRect(
6            x: 0,
7            y: frame.height - thickness,
8            width: frame.width,
9            height: thickness
10        )
11        borderStyle = .none
12        layer.addSublayer(border)
13    }
14}

Note that the extension approach does not automatically update on resize. Use the subclass approach when the text field's size can change.

Objective-C -- CALayer

The Objective-C implementation follows the same CALayer strategy. Create a UITextField subclass with a border layer:

objectivec
1// BottomBorderTextField.h
2@interface BottomBorderTextField : UITextField
3
4@property (nonatomic, strong) UIColor *bottomBorderColor;
5@property (nonatomic, assign) CGFloat bottomBorderThickness;
6
7@end
8
9// BottomBorderTextField.m
10@implementation BottomBorderTextField {
11    CALayer *_borderLayer;
12}
13
14- (instancetype)initWithFrame:(CGRect)frame {
15    self = [super initWithFrame:frame];
16    if (self) {
17        [self setupBorder];
18    }
19    return self;
20}
21
22- (instancetype)initWithCoder:(NSCoder *)coder {
23    self = [super initWithCoder:coder];
24    if (self) {
25        [self setupBorder];
26    }
27    return self;
28}
29
30- (void)setupBorder {
31    self.borderStyle = UITextBorderStyleNone;
32    _bottomBorderColor = [UIColor grayColor];
33    _bottomBorderThickness = 1.0;
34    _borderLayer = [CALayer layer];
35    _borderLayer.backgroundColor = _bottomBorderColor.CGColor;
36    [self.layer addSublayer:_borderLayer];
37}
38
39- (void)layoutSubviews {
40    [super layoutSubviews];
41    _borderLayer.frame = CGRectMake(
42        0,
43        CGRectGetHeight(self.bounds) - _bottomBorderThickness,
44        CGRectGetWidth(self.bounds),
45        _bottomBorderThickness
46    );
47}
48
49- (void)setBottomBorderColor:(UIColor *)color {
50    _bottomBorderColor = color;
51    _borderLayer.backgroundColor = color.CGColor;
52}
53
54@end

Usage in a view controller:

objectivec
1BottomBorderTextField *textField = [[BottomBorderTextField alloc]
2    initWithFrame:CGRectMake(20, 100, 280, 40)];
3textField.placeholder = @"Enter your email";
4textField.bottomBorderColor = [UIColor blueColor];
5textField.bottomBorderThickness = 2.0;
6[self.view addSubview:textField];

Xamarin.Forms -- Custom Renderer or Effect

In Xamarin.Forms, you need a custom renderer or an Effect to modify the native control's appearance. An Effect is lighter weight and does not require subclassing:

csharp
1// Shared project - the Effect class
2public class BottomBorderEffect : RoutingEffect
3{
4    public Color BorderColor { get; set; } = Color.Gray;
5    public double BorderThickness { get; set; } = 1.0;
6
7    public BottomBorderEffect() : base("MyApp.BottomBorderEffect") { }
8}
9
10// iOS platform project
11public class BottomBorderPlatformEffect : PlatformEffect
12{
13    private CALayer _borderLayer;
14
15    protected override void OnAttached()
16    {
17        var effect = Element.Effects
18            .OfType<BottomBorderEffect>()
19            .FirstOrDefault();
20        if (effect == null) return;
21
22        var textField = Control as UITextField;
23        if (textField == null) return;
24
25        textField.BorderStyle = UITextBorderStyle.None;
26
27        _borderLayer = new CALayer
28        {
29            BackgroundColor = effect.BorderColor.ToCGColor()
30        };
31        textField.Layer.AddSublayer(_borderLayer);
32        UpdateFrame(textField, effect);
33    }
34
35    protected override void OnDetached()
36    {
37        _borderLayer?.RemoveFromSuperLayer();
38    }
39
40    private void UpdateFrame(UITextField textField, BottomBorderEffect effect)
41    {
42        var thickness = (nfloat)effect.BorderThickness;
43        _borderLayer.Frame = new CGRect(
44            0,
45            textField.Bounds.Height - thickness,
46            textField.Bounds.Width,
47            thickness
48        );
49    }
50}

In XAML:

xml
1<Entry Placeholder="Enter your name">
2    <Entry.Effects>
3        <local:BottomBorderEffect BorderColor="Blue" BorderThickness="2" />
4    </Entry.Effects>
5</Entry>

For .NET MAUI (the successor to Xamarin.Forms), you can use Handlers instead of renderers, but the CALayer approach on iOS remains the same under the hood.

Styling Options

Across all frameworks, you can enhance the bottom border with a few common techniques. Change the border color on focus to highlight the active field. Animate the border thickness or color transition for a smoother user experience. In SwiftUI, animation is especially simple:

swift
1TextField("Username", text: $username)
2    .overlay(
3        Rectangle()
4            .frame(height: isFocused ? 2 : 1)
5            .foregroundColor(isFocused ? .blue : .gray)
6            .animation(.easeInOut(duration: 0.2), value: isFocused),
7        alignment: .bottom
8    )

Common Pitfalls

  • Forgetting to set borderStyle = .none in UIKit, which causes the default border to appear alongside your custom bottom line.
  • Not updating the border frame in layoutSubviews, leading to misaligned borders after rotation or Auto Layout changes.
  • Adding a new sublayer every time the view appears instead of reusing a single layer, which stacks up invisible layers.
  • Using hard-coded frame values in the extension approach that do not adapt to dynamic sizing.
  • In Xamarin, not implementing OnDetached to clean up the added layer, which can cause visual artifacts when the effect is removed.

Summary

  • In SwiftUI, use an overlay with a Rectangle pinned to alignment: .bottom for a clean, declarative solution.
  • In UIKit (Swift and Objective-C), add a CALayer sublayer and update its frame in layoutSubviews to handle resizing.
  • In Xamarin.Forms, use an Effect or custom renderer to access the native text field and apply the same CALayer technique.
  • Always set borderStyle = .none on UIKit text fields to remove the default border before adding your custom one.
  • Extract the border logic into a reusable modifier, subclass, or effect so you can apply it consistently across your app.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions