Avatar ·

Как сделать жирный и обычный текст в одном UILabel на Swift?

Всем привет! Я только начал изучать Swift и разработку под iOS. Столкнулся с такой проблемой: нужно отобразить в одном UILabel текст, где часть слов будет жирным шрифтом, а часть — обычным. Например, строка вида: "Привет, **мир**! Это **жирный** текст." Я попробовал просто установить шрифт для всего label через `label.font = UIFont.boldSystemFont(ofSize: 16)`, но тогда весь текст становится жирным. Как сделать так, чтобы только отдельные слова или фразы были выделены жирным, а остальное оставалось обычным? Вот мой текущий код, который не работает: ```swift let label = UILabel() label.text = "Привет, мир! Это жирный текст." label.font = UIFont.systemFont(ofSize: 16) // обычный шрифт для всего ``` Я слышал про NSAttributedString, но не понимаю, как его применить для этой задачи. Нужно, чтобы текст был в одной строке (не несколько label'ов). Подскажите, пожалуйста, как это сделать правильно на Swift? Заранее спасибо! <<>>

Использование NSAttributedString с UIFont

Для отображения разного форматирования в одном UILabel нужно использовать NSAttributedString. Это мощный инструмент, который позволяет применять стили к отдельным частям текста. В вашем случае мы будем задавать разные шрифты для разных диапазонов символов.

Пошаговое решение

Создадим функцию, которая принимает строку и массив слов, которые нужно сделать жирными, а возвращает NSAttributedString. Вот полный пример:

import UIKit

func makeAttributedString(fullText: String, boldWords: [String], normalFont: UIFont, boldFont: UIFont) -> NSAttributedString {
    let attributedString = NSMutableAttributedString(string: fullText)
    
    // Применяем обычный шрифт ко всему тексту
    attributedString.addAttribute(.font, value: normalFont, range: NSRange(location: 0, length: fullText.count))
    
    // Для каждого слова, которое нужно сделать жирным
    for word in boldWords {
        // Ищем все вхождения слова в тексте
        var searchRange = NSRange(location: 0, length: fullText.count)
        var foundRange = NSRange()
        
        while searchRange.location < fullText.count {
            foundRange = (fullText as NSString).range(of: word, options: [], range: searchRange)
            if foundRange.location != NSNotFound {
                // Применяем жирный шрифт к найденному диапазону
                attributedString.addAttribute(.font, value: boldFont, range: foundRange)
                // Сдвигаем searchRange для поиска следующих вхождений
                searchRange.location = foundRange.location + foundRange.length
                searchRange.length = fullText.count - searchRange.location
            } else {
                break
            }
        }
    }
    
    return attributedString
}

// Использование:
let label = UILabel()
let normalFont = UIFont.systemFont(ofSize: 16)
let boldFont = UIFont.boldSystemFont(ofSize: 16)
let text = "Привет, мир! Это жирный текст."
let boldWords = ["мир", "жирный"]

label.attributedText = makeAttributedString(fullText: text, boldWords: boldWords, normalFont: normalFont, boldFont: boldFont)
label.numberOfLines = 0 // если текст может быть многострочным
label.sizeToFit()

Более простой способ с конкретными диапазонами

Если вы знаете точные позиции слов, можно задать диапазоны вручную:

let label = UILabel()
let attributedString = NSMutableAttributedString(string: "Привет, мир! Это жирный текст.")

// Диапазон для "мир" (с 8 по 11 символ, т.е. длина 3)
attributedString.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: 16), range: NSRange(location: 8, length: 3))

// Диапазон для "жирный" (с 18 по 24 символ, длина 6)
attributedString.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: 16), range: NSRange(location: 18, length: 6))

label.attributedText = attributedString

Важные моменты

  • NSRange — это структура с полями location (начальная позиция) и length (длина диапазона).
  • Всегда используйте NSMutableAttributedString, если нужно добавить несколько атрибутов.
  • Можно комбинировать разные атрибуты: цвет (.foregroundColor), размер (.font), подчёркивание (.underlineStyle) и другие.
  • Для корректного подсчёта длины текста с эмодзи или специальными символами используйте (text as NSString).length.

Этот подход полностью решает вашу задачу и позволяет гибко управлять форматированием текста в одном UILabel.

<<>>

Альтернатива: использование вспомогательной функции с регулярными выражениями

Если вы хотите выделять жирным слова, обёрнутые в маркдаун-подобные символы (например, **жирный**), можно использовать регулярные выражения для автоматического поиска и форматирования:

import UIKit

extension UILabel {
    func applyBoldMarkdown(text: String, normalFont: UIFont, boldFont: UIFont) {
        let attributedString = NSMutableAttributedString(string: text)
        
        // Регулярное выражение для поиска **текст**
        let pattern = "\\*\\*(.*?)\\*\\*"
        let regex = try? NSRegularExpression(pattern: pattern, options: [])
        
        // Удаляем маркеры ** из текста и применяем жирный шрифт
        if let matches = regex?.matches(in: text, options: [], range: NSRange(location: 0, length: text.count)) {
            var offset = 0
            for match in matches {
                let fullRange = NSRange(location: match.range.location - offset, length: match.range.length)
                let innerRange = NSRange(location: match.range.location + 2 - offset, length: match.range.length - 4)
                
                attributedString.replaceCharacters(in: fullRange, with: (text as NSString).substring(with: innerRange))
                attributedString.addAttribute(.font, value: boldFont, range: NSRange(location: fullRange.location, length: innerRange.length))
                
                offset += 4 // убираем два ** с каждой стороны
            }
        }
        
        // Применяем обычный шрифт ко всему, что не выделено
        attributedString.addAttribute(.font, value: normalFont, range: NSRange(location: 0, length: attributedString.length))
        
        self.attributedText = attributedString
    }
}

// Использование:
let label = UILabel()
label.applyBoldMarkdown(text: "Привет, **мир**! Это **жирный** текст.", 
                        normalFont: .systemFont(ofSize: 16), 
                        boldFont: .boldSystemFont(ofSize: 16))

Этот подход удобен, если текст приходит с сервера или хранится в формате Markdown. Он автоматически обрабатывает все вхождения, избавляя от ручного указания диапазонов.