Avatar ·

Как использовать hex-значения цветов в Swift (UIColor)?

Всем привет! Я новичок в iOS-разработке и пишу на Swift. Столкнулся с проблемой: в дизайне мне дали цвета в формате HEX (например, `#FF5733`), но стандартный класс `UIColor` вроде бы не умеет работать с hex-строками напрямую. Пытался сделать так: ```swift let myColor = UIColor(hex: "#FF5733") // Ошибка! ``` Но Xcode выдает ошибку — нет такого инициализатора. Пробовал искать готовые решения в интернете, но нахожу только разные куски кода, которые не всегда работают. Например, некоторые советуют разбивать hex на RGB-компоненты вручную, но это очень неудобно, когда цветов много. Другие предлагают писать extensions, но я не уверен, как правильно это сделать, чтобы поддерживались и 6-символьные hex (без альфа-канала), и 8-символьные (с прозрачностью). Подскажите, пожалуйста, как правильно реализовать удобный способ создания `UIColor` из hex-строки? Желательно с примерами кода и пояснениями. Заранее спасибо! <<>>

Решение: Extension для UIColor с hex-инициализатором

Самый популярный и удобный способ — добавить свой инициализатор через extension к UIColor. Это позволит создавать цвета одной строкой кода. Ниже — проверенное решение, которое поддерживает форматы #RGB, #RRGGBB, #RRGGBBAA (с альфа-каналом) и вариант без решётки.

import UIKit

extension UIColor {
    /// Создаёт UIColor из hex-строки.
    /// - Parameter hex: строка вида "#RRGGBB" или "#RRGGBBAA" (решетка опциональна)
    /// - Returns: UIColor или .clear при ошибке
    convenience init?(hex: String) {
        var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
        hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")
        
        var rgb: UInt64 = 0
        guard Scanner(string: hexSanitized).scanHexInt64(&rgb) else { return nil }
        
        let length = hexSanitized.count
        let r, g, b, a: CGFloat
        
        switch length {
        case 6: // #RRGGBB
            r = CGFloat((rgb & 0xFF0000) >> 16) / 255.0
            g = CGFloat((rgb & 0x00FF00) >> 8) / 255.0
            b = CGFloat(rgb & 0x0000FF) / 255.0
            a = 1.0
        case 8: // #RRGGBBAA
            r = CGFloat((rgb & 0xFF000000) >> 24) / 255.0
            g = CGFloat((rgb & 0x00FF0000) >> 16) / 255.0
            b = CGFloat((rgb & 0x0000FF00) >> 8) / 255.0
            a = CGFloat(rgb & 0x000000FF) / 255.0
        default:
            return nil
        }
        
        self.init(red: r, green: g, blue: b, alpha: a)
    }
}

Как это работает

  • Очистка строки: удаляем пробелы и символ #, если он есть.
  • Парсинг: используем Scanner для преобразования hex-строки в 64-битное число.
  • Извлечение компонентов: с помощью битовых масок (0xFF0000 и т.д.) получаем значения красного, зеленого, синего и альфа-канала.
  • Нормализация: делим на 255, чтобы получить диапазон 0.0–1.0, который ожидает UIColor.

Примеры использования

// Без альфа-канала
let redColor = UIColor(hex: "#FF5733")!

// С альфа-каналом (полупрозрачный)
let semiTransparentBlue = UIColor(hex: "#3498DB80")! // 50% прозрачности

// Без решётки
let greenColor = UIColor(hex: "27AE60")!

// Использование для фона кнопки
button.backgroundColor = UIColor(hex: "#2ECC71")

Важные замечания

  • Инициализатор помечен как failable (?) — если строка некорректна, вернётся nil. Используйте force unwrap (!) только когда уверены в данных.
  • Для iOS 13+ можно использовать UIColor(dynamicProvider:) для адаптации к тёмной теме, комбинируя с этим extension.
  • Если вам нужно много цветов, вынесите их в константы или enum для переиспользования.

Этот подход — стандарт в iOS-сообществе и используется тысячами разработчиков. Он простой, надёжный и покрывает 99% случаев работы с hex-цветами.

<<>>

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

Если вы предпочитаете работать с целыми числами вместо строк, можно создать инициализатор, принимающий hex-значение как Int. Это удобно, когда цвета заданы в коде как константы.

extension UIColor {
    convenience init(hex: Int, alpha: CGFloat = 1.0) {
        let r = CGFloat((hex & 0xFF0000) >> 16) / 255.0
        let g = CGFloat((hex & 0x00FF00) >> 8) / 255.0
        let b = CGFloat(hex & 0x0000FF) / 255.0
        self.init(red: r, green: g, blue: b, alpha: alpha)
    }
}

// Использование:
let color = UIColor(hex: 0x3498DB)
let semiTransparent = UIColor(hex: 0xE74C3C, alpha: 0.7)

Когда это лучше?

  • Если вы храните цвета в enum или константах как Int (например, static let primaryColor = 0x2ECC71).
  • Когда не нужна поддержка строковых форматов — это быстрее и безопаснее.
  • Для задания прозрачности отдельным параметром, а не внутри hex.

Оба подхода имеют право на жизнь. Строковый вариант гибче для пользовательского ввода, числовой — компактнее для кода. Выбирайте под свою задачу!