Avatar ·

Как создать атрибутивную строку (NSAttributedString) в Swift для UILabel?

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

Создание атрибутивных строк в Swift

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

Базовый пример: жирный и обычный текст

Создадим атрибутивную строку, где слово «жирный» будет полужирным:

import UIKit

let label = UILabel()
let fullText = "Привет, мир! Это жирный текст"
let boldRange = (fullText as NSString).range(of: "жирный")

// Создаём атрибутивную строку с базовым шрифтом
let attributedString = NSMutableAttributedString(string: fullText)

// Устанавливаем жирный шрифт для нужного диапазона
let boldFont = UIFont.boldSystemFont(ofSize: 16)
attributedString.addAttribute(.font, value: boldFont, range: boldRange)

// Применяем к UILabel
label.attributedText = attributedString

Подробное объяснение ключевых моментов

  • NSMutableAttributedString — изменяемая версия NSAttributedString. Мы используем её, чтобы добавлять атрибуты поэтапно.
  • range(of:) — метод NSString, который находит диапазон подстроки. Важно привести строку к NSString, так как NSRange не совместим с Range<String.Index> напрямую.
  • addAttribute(_:value:range:) — добавляет один атрибут для указанного диапазона. В примере мы изменили только шрифт, но можно добавить и другие атрибуты (цвет, подчёркивание и т.д.).

Добавление нескольких атрибутов

Вы можете комбинировать атрибуты. Например, сделаем слово «жирный» жирным и красным, а слово «мир» — зелёным:

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

// Диапазоны
let worldRange = (attributedString.string as NSString).range(of: "мир")
let boldRange = (attributedString.string as NSString).range(of: "жирный")

// Атрибуты для "мир"
let worldAttributes: [NSAttributedString.Key: Any] = [
    .foregroundColor: UIColor.green,
    .font: UIFont.italicSystemFont(ofSize: 16)
]
attributedString.addAttributes(worldAttributes, range: worldRange)

// Атрибуты для "жирный"
let boldAttributes: [NSAttributedString.Key: Any] = [
    .foregroundColor: UIColor.red,
    .font: UIFont.boldSystemFont(ofSize: 16)
]
attributedString.addAttributes(boldAttributes, range: boldRange)

label.attributedText = attributedString

Работа с межстрочным интервалом и выравниванием

Атрибутивные строки также управляют параграфным стилем. Например, увеличим межстрочный интервал:

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 8 // 8 пикселей между строками

let attributedString = NSMutableAttributedString(string: "Привет, мир! Это жирный текст")
attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length))

label.attributedText = attributedString

Советы для начинающих

  • Всегда проверяйте, что диапазон (range) корректен. Если подстрока не найдена, range(of:) вернёт NSRange(location: NSNotFound, length: 0), что приведёт к ошибке. Добавьте проверку: guard boldRange.location != NSNotFound else { return }.
  • Используйте NSAttributedString.Key для доступа ко всем возможным атрибутам (шрифт, цвет, тень, зачёркивание и т.д.). Полный список — в документации Apple.
  • Для сложных случаев (например, разные шрифты в одной строке) удобно использовать библиотеки вроде SwiftRichString, но для базовых задач хватает стандартного API.

Теперь ваш UILabel сможет отображать текст с разными стилями. Удачи в разработке!

<<>>

Альтернативный подход: использование расширения (extension) для удобства

Если вам часто приходится создавать атрибутивные строки, можно написать небольшое расширение для String, чтобы упростить код. Вот пример:

extension String {
    func attributed(_ attributes: [NSAttributedString.Key: Any], for substring: String) -> NSAttributedString {
        let attributedString = NSMutableAttributedString(string: self)
        let range = (self as NSString).range(of: substring)
        if range.location != NSNotFound {
            attributedString.addAttributes(attributes, range: range)
        }
        return attributedString
    }
}

// Использование:
let text = "Привет, мир! Это жирный текст"
let boldAttrs: [NSAttributedString.Key: Any] = [.font: UIFont.boldSystemFont(ofSize: 16), .foregroundColor: UIColor.red]
let attributedText = text.attributed(boldAttrs, for: "жирный")
label.attributedText = attributedText

Дополнительный совет: использование HTML

Если вы привыкли к HTML-разметке, можно преобразовать HTML-строку в атрибутивную. Это удобно для сложных форматирований (списки, ссылки), но работает медленнее и требует осторожности:

let htmlString = "Привет, мир! Это <b>жирный</b> текст"
if let data = htmlString.data(using: .utf8),
   let attributedString = try? NSAttributedString(data: data,
                                                   options: [.documentType: NSAttributedString.DocumentType.html],
                                                   documentAttributes: nil) {
    label.attributedText = attributedString
}

Этот метод подходит для динамического контента из сети, но для статического текста предпочтительнее первый способ — он производительнее и безопаснее.