Recently I've started to run into an issue with UIButton.Configuration
API available starting from iOS 15 on a project targeting lower iOS versions. I have a UIButton
added using Interface Builder, and for the sake of clearly showing this issue I gave it "This is button title set from Interface Builder" title. In my view controller I have the following code which updates title of the button:
class ViewController: UIViewController {
@IBOutlet private var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 15, *) {
var configuration = UIButton.Configuration.borderless()
configuration.title = "This is button title set programmatically"
button.configuration = configuration
} else {
button.setTitle("This is button title set programmatically", for: .normal)
}
}
}
It does update title of the button, but as soon as I touch it, title changes back to "This is button title set from Interface Builder". Furthermore, if I set button "Style" property in Interface Builder to "Default" rather than "Plain", it starts to completely ignore whatever value I set to configuration.title
. Debugging shows that assigned button configuration does indeed have that updated title, but it is completely different from what it renders on the screen:
(lldb) po button.configuration?.title
▿ Optional<String>
- some : "This is button title set programmatically"
This what button title jumping back to the value from Interface Builder looks like:
And this is how the button is configured in Interface Builder:
The only fix I found so far is to update the title of this button inside of a configuration update handler, like this:
var configuration = UIButton.Configuration.borderless()
...
button.configuration = configuration
button.configurationUpdateHandler = { button in
button.configuration?.title = "This is button title set programmatically"
}
The question is do I really need to have a configuration update handler, if I only want my button to have the same title for different control states, or is it a known bug? To me it seems really unnecessary to add it if I'm only going to have one title for other states as well.
CodePudding user response:
The problem is that you have put your app into an inconsistent state. If this app is supposed to run on iOS 14, you cannot use the Plain button configuration in Interface Builder, as the iOS 14 device will have no idea what on earth to make of that and you'll crash.
What you need to do is set the interface builder button as a Default button and then, if you find you're running on iOS 15 or later, rip the button right out of the interface and replace it with a configuration-based button, along these lines:
var configuration = UIButton.Configuration.borderless()
configuration.title = "This is button title set programmatically"
let newButton = UIButton(configuration: configuration)
newButton.frame = button.frame
button.removeFromSuperview()
button = newButton
self.view.addSubview(button)
Either that or just give up on trying to use button configurations in a project intended to support iOS 14. Instead, just stick to old-fashioned setTitle(_:for:)
, etc.
Or else stop trying to support iOS 14 entirely.
In other words: Don't use button configurations at all until the only systems your app runs are systems that actually support button configurations.