UILabel и UIButton не соблюдают минимальный размер шрифта, несмотря на настройку параметров FontSizeToFitWidth и минимального масштабирования

Я работаю над проектом iOS с использованием Swift и пытаюсь обеспечить, чтобы размер шрифта как в UILabel, так и в UIButton автоматически уменьшался, когда текст не помещался. Однако, несмотря на установку adjustsFontSizeToFitWidth и minimumScaleFactor, размер шрифта, похоже, вообще не меняется.

struct UIStyling {
    static func styleLabel(label: UILabel, textLabel: String) {
        let fontName = "Montserrat-Medium"
        let fontSize: CGFloat = 18
        let minimumFontSize: CGFloat = 6

        guard let font = UIFont(name: fontName, size: fontSize) else {
            fatalError("Failed to load font \(fontName).")
        }

        let kernValue = fontSize * 0.2
        let textColor = UIColor(hex: "FFFFFF")

        label.numberOfLines = 0
        label.textAlignment = .center
        label.lineBreakMode = .byWordWrapping
        label.adjustsFontSizeToFitWidth = true
        label.minimumScaleFactor = minimumFontSize / fontSize

        let labelAttributedString = NSMutableAttributedString(
            string: textLabel,
            attributes: [
                .font: font,
                .kern: kernValue,
                .foregroundColor: textColor
            ]
        )

        label.attributedText = labelAttributedString
    }

    static func styleButton(
        _ button: UIButton,
        title: String,
        fontName: String = "Montserrat-Medium",
        fontSize: CGFloat = 18,
        minimumFontSize: CGFloat = 6,
        cornerRadius: CGFloat = 20,
        backgroundColor: UIColor,
        titleColor: UIColor
    ) {
        guard let font = UIFont(name: fontName, size: fontSize) else {
            fatalError("Failed to load font \(fontName).")
        }

        button.titleLabel?.adjustsFontSizeToFitWidth = true
        button.titleLabel?.minimumScaleFactor = minimumFontSize / fontSize
        button.titleLabel?.numberOfLines = 0
        button.titleLabel?.lineBreakMode = .byClipping

        button.setTitle(title, for: .normal)
        button.titleLabel?.font = font
        button.setTitleColor(titleColor, for: .normal)

        button.backgroundColor = backgroundColor
        button.layer.cornerRadius = cornerRadius
    }
}

func buttonStyles(for sceneType: SceneType) -> (fontName: String, backgroundColors: [(UIColor, UIColor)], textColors: (UIColor, UIColor)) {
    switch sceneType {
    case .egypt:
        return (
            fontName: "Montserrat-Medium",
            backgroundColors: [
                (UIColor(hex: "#FFD700").withAlphaComponent(0.8), UIColor(hex: "#333333")),
                (UIColor(hex: "#006400").withAlphaComponent(0.8), UIColor(hex: "#FFFFFF"))
            ],
            textColors: (UIColor(hex: "#333333"), UIColor(hex: "#FFFFFF"))
        )
    case .future:
        return (
            fontName: "Montserrat-Medium",
            backgroundColors: [
                (UIColor(hex: "#1E90FF").withAlphaComponent(0.8), UIColor(hex: "#FFFFFF")),
                (UIColor(hex: "#D3D0D0").withAlphaComponent(0.8), UIColor(hex: "#090909"))
            ],
            textColors: (UIColor(hex: "#FFFFFF"), UIColor(hex: "#090909"))
        )
    case .main:
        return (
            fontName: "Montserrat-Medium",
            backgroundColors: [
                (UIColor(hex: "#FFD700").withAlphaComponent(0.8), UIColor(hex: "#333333")),
                (UIColor(hex: "#1E90FF").withAlphaComponent(0.8), UIColor(hex: "#FFFFFF"))
            ],
            textColors: (UIColor(hex: "#333333"), UIColor(hex: "#FFFFFF"))
        )
    }
}

func updateUI() {
    let currentScene = storyBrain.getCurrentScene()

    UIStyling.styleLabel(label: textLabel, textLabel: currentScene.title)

    let styles = buttonStyles(for: currentScene.sceneType)

    UIStyling.styleButton(button1,
                          title: currentScene.button1,
                          fontName: styles.fontName,
                          backgroundColor: styles.backgroundColors[0].0,
                          titleColor: styles.textColors.0)

    UIStyling.styleButton(button2,
                          title: currentScene.button2,
                          fontName: styles.fontName,
                          backgroundColor: styles.backgroundColors[1].0,
                          titleColor: styles.textColors.1)

    button1.layer.cornerRadius = Style.cornerRadius
    button2.layer.cornerRadius = Style.cornerRadius

    background.image = UIImage(named: currentScene.backgroundImage)

    print("Label size: \(textLabel.frame.size)")
    print("Minimum scale factor: \(textLabel.minimumScaleFactor)")
    print("Font size: \(textLabel.font.pointSize)")
}
func updateUI() {
    let currentScene = storyBrain.getCurrentScene()

    UIStyling.styleLabel(label: textLabel, textLabel: currentScene.title)

    let styles = buttonStyles(for: currentScene.sceneType)

    UIStyling.styleButton(button1,
                          title: currentScene.button1,
                          fontName: styles.fontName,
                          backgroundColor: styles.backgroundColors[0].0,
                          titleColor: styles.textColors.0)

    UIStyling.styleButton(button2,
                          title: currentScene.button2,
                          fontName: styles.fontName,
                          backgroundColor: styles.backgroundColors[1].0,
                          titleColor: styles.textColors.1)
    
    button1.layer.cornerRadius = Style.cornerRadius
    button2.layer.cornerRadius = Style.cornerRadius

    background.image = UIImage(named: currentScene.backgroundImage)

    print("Label size: \(textLabel.frame.size)")
    print("Minimum scale factor: \(textLabel.minimumScaleFactor)")
    print("Font size: \(textLabel.font.pointSize)")

    // ...
}

Описание проблемы:

  • Проблема: размер шрифта как в UILabel, так и в UIButton не меняется, даже если текст слишком длинный и не помещается.
  • Ожидаемое поведение: если текст не помещается, размер шрифта должен уменьшиться в соответствии с настройкой minimumScaleFactor.
  • Текущее поведение: размер шрифта остается прежним, игнорируя свойства adjustsFontSizeToFitWidth и minimumScaleFactor.

Что я пробовал:

  1. Программная установка adjustsFontSizeToFitWidth и minimumScaleFactor: как показано в приведенном выше коде, я установил эти свойства, но размер шрифта не уменьшается.
  2. Тестирование с разными значениями minimumFontSize: я пробовал разные значения minimumFontSize, но это не имело никакого значения.
  3. Проверка загрузки шрифтов: я подтвердил, что шрифты правильно загружены и применены.
  4. Проверка ограничений автоматического макета: меткам и кнопкам достаточно места, и ограничения кажутся нормальными.

Вопросы:

  1. Почему minimumScaleFactor игнорируется? Есть ли что-то в моем коде, что может помешать этому работать?
  2. Есть ли какой-то конкретный параметр в Interface Builder или программно, который мне может не хватать? Может ли это быть проблемой ограничений?
  3. Может ли это быть связано с ошибкой в ​​целевой версии iOS? Или что-то не так с тем, как я применяю стили?

Среда:

  • Версия Xcode: 15.4 (15F31d)
  • Свифт-версия: 5.10
  • Версия iOS: минимальное количество развертываний — 15.3.

1
50
1

Ответ:

Решено

Чтобы получить автоматическое масштабирование шрифта, .lineBreakMode должен быть установлен на отсечение или один из режимов усечения — Head, Middle или (чаще всего) Tail:

//label.lineBreakMode = .byWordWrapping
label.lineBreakMode = .byTruncatingTail