Avatar ·

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

Всем привет! Я новичок в Swift и пытаюсь сделать текст с разным форматированием в UILabel. Например, чтобы одно слово было жирным, другое — красным, а третье — с подчеркиванием. Я знаю, что для этого есть NSAttributedString, но не могу разобраться, как его использовать. Пробовал написать такой код, но он не работает: ```swift let label = UILabel() label.text = "Привет, мир!" label.textColor = UIColor.red // хочу только часть текста красной ``` Потом пытался сделать через NSAttributedString, но запутался в синтаксисе. Вот что я написал: ```swift let attributedString = NSAttributedString(string: "Привет, мир!") attributedString.addAttribute(.foregroundColor, value: UIColor.red, range: NSRange(location: 0, length: 7)) label.attributedText = attributedString ``` Но Xcode выдаёт ошибку: "Cannot use mutating member on immutable value: 'attributedString' is a 'let' constant". Я не понимаю, в чём проблема. Как правильно создать и применить атрибутированную строку? Нужно ли подключать какие-то дополнительные библиотеки? Объясните, пожалуйста, пошагово для новичка. Мне нужно, чтобы в одной строке были разные цвета, шрифты и стили. Желательно с примерами кода, которые можно скопировать и сразу использовать. <<>>

Правильное создание NSAttributedString в Swift

Проблема вашего кода в том, что NSAttributedString является неизменяемым (immutable) типом. Вы не можете изменить его после создания через let. Для изменения атрибутов нужно использовать изменяемую версию — NSMutableAttributedString. Вот правильный подход:

import UIKit

// 1. Создаем изменяемую атрибутированную строку
let attributedString = NSMutableAttributedString(string: "Привет, мир!")

// 2. Добавляем атрибуты с помощью метода addAttribute
attributedString.addAttribute(.foregroundColor, 
                              value: UIColor.red, 
                              range: NSRange(location: 0, length: 7))

// 3. Применяем к UILabel
let label = UILabel()
label.attributedText = attributedString

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

Чтобы применить несколько стилей к разным частям строки, используйте несколько вызовов addAttribute. Вот пример с жирным шрифтом, цветом и подчеркиванием:

let text = "Привет, мир! Это Swift."
let attributedString = NSMutableAttributedString(string: text)

// Жирный шрифт для первого слова
let boldFont = UIFont.boldSystemFont(ofSize: 18)
attributedString.addAttribute(.font, 
                              value: boldFont, 
                              range: NSRange(location: 0, length: 6))

// Красный цвет для "мир"
attributedString.addAttribute(.foregroundColor, 
                              value: UIColor.red, 
                              range: NSRange(location: 8, length: 3))

// Подчеркивание для "Swift"
attributedString.addAttribute(.underlineStyle, 
                              value: NSUnderlineStyle.single.rawValue, 
                              range: NSRange(location: 17, length: 5))

// Дополнительно: меняем фон для части текста
attributedString.addAttribute(.backgroundColor, 
                              value: UIColor.yellow, 
                              range: NSRange(location: 0, length: 6))

label.attributedText = attributedString

Удобный способ через словарь атрибутов

Можно использовать метод setAttributes для применения сразу набора атрибутов к диапазону:

let attributes: [NSAttributedString.Key: Any] = [
    .font: UIFont.boldSystemFont(ofSize: 16),
    .foregroundColor: UIColor.blue,
    .underlineStyle: NSUnderlineStyle.double.rawValue
]

let range = NSRange(location: 0, length: 6)
attributedString.setAttributes(attributes, range: range)

Работа с NSRange и эмодзи

Важно: NSRange работает с UTF-16 кодировкой, поэтому для строк с эмодзи или специальными символами нужно быть осторожным. Используйте (text as NSString).range(of: "слово") для безопасного поиска:

let text = "Привет 😊 мир!"
let attributedString = NSMutableAttributedString(string: text)

if let range = text.range(of: "😊") {
    let nsRange = NSRange(range, in: text)
    attributedString.addAttribute(.font, 
                                  value: UIFont.systemFont(ofSize: 30), 
                                  range: nsRange)
}

Расширение для удобства

Чтобы не писать каждый раз NSRange, сделайте небольшое расширение:

extension NSMutableAttributedString {
    func addAttribute(_ key: NSAttributedString.Key, value: Any, for substring: String) {
        let range = (self.string as NSString).range(of: substring)
        if range.location != NSNotFound {
            self.addAttribute(key, value: value, range: range)
        }
    }
}

// Использование:
let attrStr = NSMutableAttributedString(string: "Hello World")
attrStr.addAttribute(.foregroundColor, value: UIColor.red, for: "World")

Теперь ваш код будет работать без ошибок. Главное — всегда используйте NSMutableAttributedString для изменений и не забывайте про NSRange.

<<>>

Альтернативный подход: использование HTML

Если вам нужно быстро создать сложное форматирование, можно использовать HTML-строку и преобразовать её в NSAttributedString. Это удобно, когда стилизация известна из веб-источника или её проще описать в HTML:

let htmlString = """
Привет, 
мир!
"""

if let data = htmlString.data(using: .utf8),
   let attributedString = try? NSAttributedString(
       data: data,
       options: [.documentType: NSAttributedString.DocumentType.html],
       documentAttributes: nil
   ) {
    label.attributedText = attributedString
}

Использование сторонних библиотек

Для ещё более простого синтаксиса можно использовать библиотеку SwiftRichString (через CocoaPods или SPM). Пример:

import SwiftRichString

let style = Style {
    $0.font = UIFont.boldSystemFont(ofSize: 16)
    $0.color = UIColor.red
    $0.underline = .single
}

let attributedString = "Привет, мир!".set(style: style)
label.attributedText = attributedString

Этот подход сокращает код и делает его более читаемым, особенно при работе с множеством стилей.