11

I'm building an iOS app for a class, and I'm following along some instructions. The Edit button is connected to toggleEditingMode, but when I change the text, for some reason the font size is reset to 17, even though it's 30 in the storyboard editor.

I've tried changing the font size, if I print the current font size after executing setTitle it still says 30, so it seems like it must be happening outside of setTitle, but it only triggers if I use setTitle. Help!

class ItemsViewController: UITableViewController {
    var choreStore: ChoreStore!
    var roommateStore: RoommateStore!
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return choreStore.allChores.count
    }
        
    override func tableView(_ tableView: UITableView,
            cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // Create an instance of UITableViewCell with default appearance
        let cell = tableView.dequeueReusableCell(withIdentifier: "chore", for: indexPath) as! ChoreCell

        // Set the text on the cell with the description of the item
        // that is at the nth index of items, where n = row this cell
        // will appear in on the table view
        let chore = choreStore.allChores[indexPath.row]

        cell.title?.text = chore.title
        cell.turn?.text = "\(chore.whoseTurn())'s Turn"
        cell.completed?.text = chore.completedString()
        cell.completed?.textColor = chore.isOverdue ? .red : .black

        return cell
    }
    
    @IBAction func addNewItem(_ sender: UIButton) {

    }

    @IBAction func toggleEditingMode(_ sender: UIButton) {
        setEditing(!isEditing, animated: true)
        sender.setTitle(isEditing ? "Done" : "Edit", for: .normal)
    }
}
HangarRash
  • 7,314
  • 5
  • 5
  • 32

4 Answers4

26

Change your button style to Default:

Setting style

Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77
Tulio Parreiras
  • 361
  • 3
  • 5
1

I use the following extension:

import UIKit

extension UIButton {
    func setText(_ text: String) {
        if let font = titleLabel?.font {
            let attributes: [NSAttributedString.Key: Any] = [
                .font: font
            ]
            let attributedTitle = NSAttributedString(string: text, attributes: attributes)
            
            setAttributedTitle(attributedTitle, for: .normal)
        }
    }
}

Migo
  • 206
  • 2
  • 4
0

Better solution on another question

If you're in a hurry, @matt's answer on Change just the string of an AttributedString (e.g. UIButton configuration attributedTitle) is better than what I propose here.


My take

Buttons using the new-ish API UIButton.Configuration will reset their label's font to a default UICTFontTextStyleShortBody font when updating their title using setTitle(_:for:). This doesn't seems to be documented behavior, but is easily reproducible.

Note : If you created a button in Interface Builder (a storyboard for exemple) using a "Style" value other than "Default", your button is using UIButton.Configuration under the hood.

To prevent losing the font set in Interface Builder, I'm using the following extension on UIButton:

import UIKit

extension UIButton {
    /// Sets the title to use for the `.normal` state, while keeping the current font of the button.
    ///
    /// https://stackoverflow.com/questions/69869506/why-does-uibutton-settitle-change-the-font-size
    func setTitleKeepingFont(_ title: String?) {
        guard
            configuration != nil,
            let title,
            let currentFont = titleLabel?.font
        else {
            setTitle(title, for: .normal)
            return
        }

        let attributes: [NSAttributedString.Key: Any] = [
            .font: currentFont
        ]
        let attributedTitle = NSAttributedString(string: title, attributes: attributes)
        setAttributedTitle(attributedTitle, for: .normal)
    }
}

This is a improvement on @Migo's solution https://stackoverflow.com/a/76588943/404321. It defers the work to UIKit for default-style buttons, for which changing the title doesn't seem to affect the font (and thus a fix is not necessary).

Note also that this only works if you're changing your UIButton title for the .normal UIControl.State. If you want to have a different title for each state you should probably use configurationUpdateHandler, and manage the font in this handler like we do in the extension.

Guillaume Algis
  • 10,705
  • 6
  • 44
  • 72
-1

If you want to retain a filled, tinted, or grey configuration, a better idea is to use

button.titleLabel?.text = "Lorem ipsum"
David
  • 87
  • 12
  • 1
    This solution will not update the button frame according to the text width. – J.Doe May 07 '23 at 18:17
  • 1
    The documentation also states that "To set the actual text of the label, use `setTitle(_:for:)` (`button.titleLabel.text` does not let you set the text)". So I wouldn't advise using this API even if it *seems* to work. – Guillaume Algis Aug 02 '23 at 12:37