UIButton.Configuration, which overrides button.titleLabel?.font. To change the font, assign an AttributedString with the desired UIFont or Font container to button.configuration?.attributedTitle, or switch the button style to Default (Legacy) in Storyboard if you prefer using titleLabel?.font.Why this happens
Setting a font on a UIButton typically fails or gets ignored for two distinct reasons in modern iOS development. First, the old property myButton.font was deprecated in iOS 3.0 and subsequently removed from modern Swift SDK interfaces. Calling .font directly on a UIButton instance causes compiler errors such as Value of type 'UIButton' has no member 'font'.
Second, developers who switch to button.titleLabel?.font = UIFont.systemFont(...) often find that their font changes are completely ignored at runtime. This happens because iOS 15 introduced UIButton.Configuration. If your button uses one of the modern configuration styles—such as Plain, Tinted, Gray, or Filled—UIKit manages label properties internally through its configuration pipeline. Every time the control redraws or updates state, UIKit wipes out direct mutations to titleLabel and reapplies the attributes declared in UIButton.Configuration.
If you created the button in Interface Builder (Xcode Storyboards or XIB files), Xcode defaults new buttons to the Plain style rather than the legacy Default style. As long as a button uses modern configuration styles, direct assignments to titleLabel.font or titleLabel.textColor will not appear on screen.
How to fix it
1. Use UIButton.Configuration with AttributeContainer (iOS 15+)
For any modern UIKit project running on iOS 15 or later, use UIButton.Configuration to set fonts. Build an AttributeContainer containing your custom or system font, then apply it to the configuration’s attributedTitle.
import UIKit
func configureModernButton(button: UIButton) {
var config = button.configuration ?? UIButton.Configuration.filled()
// Create the typography attributes
var container = AttributeContainer()
container.font = UIFont.systemFont(ofSize: 18, weight: .semibold)
// Apply title text and typography together
config.attributedTitle = AttributedString("Submit Order", attributes: container)
// Assign back to the button
button.configuration = config
}
If you need dynamic font scaling that responds to Dynamic Type, use UIFontMetrics:
var container = AttributeContainer()
let baseFont = UIFont.systemFont(ofSize: 17, weight: .bold)
container.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: baseFont)
config.attributedTitle = AttributedString("Continue", attributes: container)
button.configuration = config
2. Update Font via configurationUpdateHandler
When your button changes state (such as highlighted, disabled, or selected) and needs to preserve or alter typography dynamically, configure the font inside configurationUpdateHandler:
import UIKit
button.configuration = UIButton.Configuration.plain()
button.configurationUpdateHandler = { button in
var config = button.configuration
var container = AttributeContainer()
let fontSize: CGFloat = button.isHighlighted ? 15 : 16
container.font = UIFont.systemFont(ofSize: fontSize, weight: .medium)
config?.attributedTitle = AttributedString("Save Changes", attributes: container)
button.configuration = config
}
This ensures that UIKit does not reset your font during state transitions.
3. Use titleLabel.font with Legacy Buttons
If your button does not use UIButton.Configuration, or if you explicitly want to use the legacy UIKit button hierarchy, ensure the button is initialized without a configuration.
In code:
import UIKit
// Create a button without a configuration
let legacyButton = UIButton(type: .system)
legacyButton.setTitle("Tap Here", for: .normal)
// Direct access works on non-configured buttons
legacyButton.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .medium)
In Storyboards or XIBs:
1. Open your storyboard and select the target UIButton.
2. Open the Attributes Inspector (⌥ ⌘ 5).
3. Look at the Style dropdown at the top. Change it from Plain, Gray, Tinted, or Filled back to Default.
4. Set the Font field in Interface Builder, or assign button.titleLabel?.font in your view controller’s viewDidLoad().
4. Apply Custom Fonts by PostScript Name
When using custom bundled .otf or .ttf fonts, construct the UIFont using the exact PostScript name, then pass it to the AttributeContainer:
import UIKit
func applyCustomFont(to button: UIButton) {
guard let customFont = UIFont(name: "AvenirNext-DemiBold", size: 18) else {
return
}
var config = button.configuration ?? UIButton.Configuration.plain()
var container = AttributeContainer()
container.font = customFont
config.attributedTitle = AttributedString("Explore", attributes: container)
button.configuration = config
}
Make sure the custom font file is listed under the UIAppFonts key (Fonts provided by application) in your Info.plist and included in your app target’s Copy Bundle Resources build phase.
How to prevent it
- Avoid accessing
button.titleLabel?.fonton buttons that have non-nilbutton.configurationinstances. - Check the Style property in the Interface Builder Attributes Inspector whenever a programmatic font does not appear; change it to Default if you are targeting legacy code.
- Standardize on
UIButton.ConfigurationandAttributedStringfor all new UIKit code targeting iOS 15 and later. - Do not use
button.font, which was removed in early iOS SDK revisions. - Always set button titles and typography together via
config.attributedTitleorbutton.setTitle(_:for:)rather than mutatingbutton.titleLabel?.textdirectly.
FAQ
Why does setting button.titleLabel?.text also fail?
In UIKit, titleLabel is managed directly by the button instance. Setting button.titleLabel?.text = "..." will be overwritten whenever the button state updates; you should always use button.setTitle("Text", for: .normal) for legacy buttons or configuration.title / configuration.attributedTitle for modern buttons.
How do I check if my button uses UIButton.Configuration in code?
Inspect button.configuration. If button.configuration is not nil, the button uses the modern configuration pipeline, and any direct adjustments made to button.titleLabel?.font will be overwritten by UIKit.
Can I use SwiftUI Font with UIButton.Configuration?
No, UIKit’s AttributeContainer.font expects a UIFont instance, not a SwiftUI Font. Use UIFont.systemFont(ofSize:weight:) or UIFontMetrics to define your typography.
Parts of this guide are adapted from content licensed under CC BY-SA 3.0; this article is shared under the same license.
