Fix: Value of Type ‘UIButton’ Has No Member ‘text’ in Swift

Fix: Value of Type 'UIButton' Has No Member 'text' in Swift

Written by

in

Quick answer: Unlike UILabel, UIButton does not have a ‘text’ property because it supports state-dependent titles. To change a button’s text programmatically, call button.setTitle(“New Title”, for: .normal) or update its button.configuration?.title in iOS 15+. Ensure this code runs inside a method like viewDidLoad(), not in the bare class scope.

Why this happens

In UIKit, developers frequently attempt to update a button label using button.text = "Submit" after working with UILabel or UITextField. Swift rejects this assignment with the compiler error Value of type 'UIButton' has no member 'text' because UIButton does not expose a mutable text property.

UIButton inherits from UIControl, which operates on dynamic states such as .normal, .highlighted, .disabled, and .selected. Because a single button can display different strings depending on user interaction, UIKit manages title text through dedicated setter methods or configuration structs rather than a simple string property on the button instance.

If you also encounter an Expected declaration compiler error alongside this issue, you placed execution code directly inside the class body instead of enclosing it within a method, initializer, or lifecycle hook like viewDidLoad().

How to fix it

1. Use setTitle(_:for:) for Standard Buttons

The primary way to set or update button text across UIKit is calling setTitle(_:for:). Supply the string and the target UIControl.State (typically .normal).

Make sure your button is declared either programmatically or as an @IBOutlet, and place the title assignment inside a valid method scope:

import UIKit

class CurrencyViewController: UIViewController {
    @IBOutlet weak var currencySelector: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Set the visible title for standard state
        currencySelector.setTitle("foobar", for: .normal)
    }
}

If you want the button to display a different label while the user is actively pressing it, you can specify additional states:

currencySelector.setTitle("Loading...", for: .highlighted)
currencySelector.setTitle("Unavailable", for: .disabled)

2. Update the Title Using UIButton.Configuration (iOS 15+)

If your project targets iOS 15 or later and uses modern button styles (.filled(), .tinted(), .plain(), or .gray()), modifying button titles through UIButton.Configuration is the recommended approach. Assigning via setTitle(_:for:) can sometimes be ignored or overridden if a configuration object is active.

Read the current configuration, mutate the title property, and reassign it to the button:

import UIKit

class SettingsViewController: UIViewController {
    @IBOutlet weak var actionButton: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        updateButtonTitle("Confirm Payment")
    }

    func updateButtonTitle(_ newTitle: String) {
        var config = actionButton.configuration ?? UIButton.Configuration.filled()
        config.title = newTitle
        actionButton.configuration = config
    }
}

For dynamic state updates when using configurations, assign a configurationUpdateHandler:

actionButton.configurationUpdateHandler = { button in
    var config = button.configuration
    config?.title = button.isHighlighted ? "Processing..." : "Continue"
    button.configuration = config
}

3. Resolve the ‘Expected Declaration’ Error

The Expected declaration error occurs when executable statements sit directly in the class definition. In Swift, class bodies can only contain property declarations, type declarations, and method definitions.

Incorrect:

class ProfileViewController: UIViewController {
    @IBOutlet weak var submitButton: UIButton!

    // Compiler Error: Expected declaration
    submitButton.setTitle("Save", for: .normal)
}

Correct:

class ProfileViewController: UIViewController {
    @IBOutlet weak var submitButton: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Runs properly during the view lifecycle
        submitButton.setTitle("Save", for: .normal)
    }
}

4. Updating the Title from an IBAction

If you want to toggle the title when the user taps the button (for example, switching between “Play” and “Pause”), you can access the button directly through the action method’s sender parameter:

@IBAction func mediaButtonTapped(_ sender: UIButton) {
    if sender.title(for: .normal) == "Play" {
        sender.setTitle("Pause", for: .normal)
    } else {
        sender.setTitle("Play", for: .normal)
    }
}

If your @IBAction was created with sender: Any, cast sender to UIButton before calling setTitle(_:for:):

@IBAction func genericButtonTapped(_ sender: Any) {
    guard let button = sender as? UIButton else {
        return
    }
    button.setTitle("Done", for: .normal)
}

5. Applying Formatted Text with Attributed Strings

If your button uses custom typography, colors, or kerning, setting a plain string will discard custom formatting. Use setAttributedTitle(_:for:) instead:

let attributes: [NSAttributedString.Key: Any] = [
    .font: UIFont.boldSystemFont(ofSize: 16),
    .foregroundColor: UIColor.systemBlue
]
let attributedTitle = NSAttributedString(string: "Checkout", attributes: attributes)

currencySelector.setAttributedTitle(attributedTitle, for: .normal)

How to prevent it

  • Remember that UIButton relies on control states; use setTitle(_:for:) or configuration.title rather than treating it like a UILabel.
  • Keep all procedural and configuration logic inside lifecycle methods (viewDidLoad, viewWillAppear) or custom helper functions.
  • Check whether a storyboard button uses the “Default” style or an iOS 15+ style (“Filled”, “Tinted”, “Gray”, “Plain”) in the Attributes Inspector, and match your code approach to either traditional methods or UIButton.Configuration.
  • When modifying button properties from a tap handler, set the @IBAction sender type to UIButton to avoid manual type casting.

FAQ

Why does my button title not change after calling setTitle(_:for:)?

If your button uses modern iOS 15+ button configurations (such as Filled or Tinted in Interface Builder), setTitle(_:for:) may be overridden by the active configuration. Update button.configuration?.title or reset the button’s style to Default in the storyboard.

Can I access the button’s underlying label using titleLabel?.text?

While button.titleLabel?.text = "Title" compiles, UIKit discourages direct assignment on titleLabel because the button can reset the label text during internal layout passes. Always use setTitle(_:for:) or UIButton.Configuration.

How do I retrieve the current button text programmatically?

Call button.title(for: .normal) or button.currentTitle to retrieve the current string for the default state, or inspect button.configuration?.title if modern configurations are enabled.

Sources & credits

Parts of this guide are adapted from content licensed under CC BY-SA 3.0; this article is shared under the same license.