Swift programming
iOS development
button customization
SwiftUI
underlined text

Underline button text in Swift

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Underlined button text is a common way to present link-like actions such as “Forgot password” or “View terms.” In Swift, the implementation depends on whether you are using UIKit or SwiftUI, but the core idea is the same: apply underline styling to the button label rather than faking a button with a plain label. The most reliable solutions keep the text style reusable and handle interaction states cleanly.

UIKit: Use an Attributed Title

In UIKit, the standard solution is to assign an NSAttributedString to the button for the relevant control state. That gives you direct control over underline style, font, and color.

swift
1import UIKit
2
3final class LoginViewController: UIViewController {
4    private let forgotButton = UIButton(type: .system)
5
6    override func viewDidLoad() {
7        super.viewDidLoad()
8        view.backgroundColor = .systemBackground
9
10        forgotButton.translatesAutoresizingMaskIntoConstraints = false
11        forgotButton.setAttributedTitle(makeUnderlinedTitle("Forgot password?", color: .systemBlue), for: .normal)
12        forgotButton.addTarget(self, action: #selector(handleForgotTap), for: .touchUpInside)
13
14        view.addSubview(forgotButton)
15        NSLayoutConstraint.activate([
16            forgotButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
17            forgotButton.centerYAnchor.constraint(equalTo: view.centerYAnchor)
18        ])
19    }
20
21    private func makeUnderlinedTitle(_ text: String, color: UIColor) -> NSAttributedString {
22        NSAttributedString(
23            string: text,
24            attributes: [
25                .underlineStyle: NSUnderlineStyle.single.rawValue,
26                .foregroundColor: color,
27                .font: UIFont.preferredFont(forTextStyle: .body)
28            ]
29        )
30    }
31
32    @objc private func handleForgotTap() {
33        print("forgot tapped")
34    }
35}

This is the most direct answer when the question is “how do I underline button text in Swift.”

Style Different Button States Explicitly

A button is not just one visual state. If you only style .normal, the underline may disappear or feel inconsistent when the button is highlighted, disabled, or selected. A small helper keeps that styling consistent.

swift
1import UIKit
2
3extension UIButton {
4    func applyLinkStyle(title: String, normalColor: UIColor, highlightedColor: UIColor) {
5        let normal = NSAttributedString(
6            string: title,
7            attributes: [
8                .underlineStyle: NSUnderlineStyle.single.rawValue,
9                .foregroundColor: normalColor
10            ]
11        )
12
13        let highlighted = NSAttributedString(
14            string: title,
15            attributes: [
16                .underlineStyle: NSUnderlineStyle.single.rawValue,
17                .foregroundColor: highlightedColor
18            ]
19        )
20
21        setAttributedTitle(normal, for: .normal)
22        setAttributedTitle(highlighted, for: .highlighted)
23    }
24}

This avoids duplicating inline attributed-string code across view controllers and makes future design changes easier.

SwiftUI: Underline the Text Inside the Button

SwiftUI is simpler because the label can just be a Text view with an underline modifier.

swift
1import SwiftUI
2
3struct LinkButtonView: View {
4    var body: some View {
5        Button(action: {
6            print("tapped")
7        }) {
8            Text("View terms")
9                .underline(true, color: .blue)
10                .foregroundStyle(.blue)
11        }
12    }
13}
14
15#Preview {
16    LinkButtonView()
17}

Using a custom label closure is usually better than the short Button("Title") form because it gives you precise control over styling.

Use AttributedString for Richer SwiftUI Labels

If the label needs more than a simple underline, modern SwiftUI can use AttributedString.

swift
1import SwiftUI
2
3struct UnderlinedActionView: View {
4    private var label: AttributedString {
5        var text = AttributedString("Manage subscription")
6        text.underlineStyle = .single
7        text.foregroundColor = .blue
8        return text
9    }
10
11    var body: some View {
12        Button(action: {
13            print("manage tapped")
14        }) {
15            Text(label)
16        }
17    }
18}

This becomes useful when only part of the text needs styling, or when the label is built from localized content with richer formatting rules.

Treat It as an Interactive Control, Not Decoration

An underline makes the text look like a link, but the element is still a button. That means normal UI control rules still apply:

  • give it a tap target large enough to use comfortably
  • preserve enough contrast between text and background
  • keep the wording action-oriented
  • make sure accessibility still communicates what the action does

If the button is important, underline alone is not enough. Color, spacing, and placement still matter.

Prefer Reusable Styling in Real Apps

If the same underlined style appears in several screens, centralize it instead of rebuilding the attributed string everywhere.

swift
1import UIKit
2
3enum LinkButtonFactory {
4    static func make(title: String, target: Any?, action: Selector) -> UIButton {
5        let button = UIButton(type: .system)
6        button.applyLinkStyle(title: title, normalColor: .systemBlue, highlightedColor: .systemBlue.withAlphaComponent(0.6))
7        button.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body)
8        button.addTarget(target, action: action, for: .touchUpInside)
9        return button
10    }
11}

That approach keeps underline thickness, color choice, and interaction behavior consistent across the app.

Common Pitfalls

  • Calling setTitle after setAttributedTitle, which removes the underline styling you just applied.
  • Styling only the normal state and forgetting highlighted or disabled appearances.
  • Using underline as the only cue even when color contrast or touch target size is weak.
  • Repeating ad hoc attributed-string code in every screen instead of reusing a helper or factory.
  • Assuming SwiftUI button styling automatically preserves custom underline behavior in every context.

Summary

  • In UIKit, underline button text with setAttributedTitle and an NSAttributedString.
  • In SwiftUI, underline the Text inside the button label.
  • Treat interaction states explicitly so the style stays consistent when the button is pressed or disabled.
  • Reuse styling helpers when the same link-style button appears across multiple screens.
  • Underlined text should still behave like a proper accessible button, not just styled decoration.

Course illustration
Course illustration

All Rights Reserved.