skip to Main Content

Suppose I have a configuration-based button, with an attributed title:

let button = UIButton(configuration: .plain())
let font = UIFont(name: "Georgia", size: 16)
button.configuration?.attributedTitle = AttributedString(
    "Hello", attributes: AttributeContainer.font(font!)
)

If I later come along and set the configuration’s title to a different title, the attribute information is lost. For example:

button.configuration?.title = "Goodbye"
// Button title is no longer in Georgia font!

What I want to do here is to replace the text of an attributed string title without disturbing its attributes. But Swift’s AttributedString doesn’t seem to provide a way to do that. What’s the correct approach here?

2

Answers


  1. Chosen as BEST ANSWER

    Changing the text of an AttributedString is remarkably tricky. You have to replace the contents of the attributed string's character view — its characters property. To make things even harder, you cannot do this simply by assigning another string! For example, this won't compile:

    button.configuration?.attributedTitle?.characters = "Goodbye" // error
    

    Nor is it sufficient to derive the character view from a simple string. This doesn't compile either:

    button.configuration?.attributedTitle?.characters = "Goodbye".characters // error
    

    This is because the separate character view of a simple string no longer exists; you're still trying to assign a String into a character view, and we already know you can't do that.

    Instead, you can make another AttributedString and assign its character view into the button's attributed title's character view:

    button.configuration?.attributedTitle?.characters = AttributedString("Goodbye").characters
    

    That replaces the button's title without disturbing the button's title's style attributes. This is such a useful thing to be able to do that I've made a little utility extension on UIButton that covers all the cases:

    extension UIButton {
        func replaceTitle(_ newTitle: String) {
            guard configuration != nil else {
                setTitle(newTitle, for: .normal)
                return
            }
            guard configuration?.attributedTitle != nil else {
                configuration?.title = newTitle
                return
            }
            configuration?.attributedTitle?.characters = AttributedString(newTitle).characters
        }
    }
    

  2. If the attributes of your title will always apply to the entire title then an alternate approach is to use the titleTextAttributesTransformer property of the button configuration instead of making use of attributedTitle. Then you can simply change the button’s title and those attributes will be applied to the whole title.

    For example:

    var buttonConfig = UIButton.Configuration.plain()
    buttonConfig.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { incoming in
        var outgoing = incoming
        outgoing.font = UIFont(name: "Georgia", size: 16)
        outgoing.foregroundColor = UIColor.black
        return outgoing
    }
    buttonConfig.title = "Hello"
    let button = UIButton(configuration: buttonConfig)
    

    Then later you can simply do:

    button.configuration?.title = "Goodbye"
    

    and the new title will have the same font and other attributes of the original title.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search