Avatar ·

Как использовать hex-коды цветов (HEX Color) в Swift и UIColor?

Привет! Я новичок в Swift и iOS-разработке, и у меня возникла проблема с цветами. В веб-дизайне я привык использовать hex-коды цветов, например `#FF5733` для оранжевого. Но в Xcode, при работе с `UIColor`, я вижу только инициализаторы вроде `UIColor.red` или `UIColor(red: 1.0, green: 0.34, blue: 0.2, alpha: 1.0)`. Это очень неудобно — каждый раз конвертировать hex в десятичные значения. Я пробовал написать простое расширение, но оно не работает. Вот мой код: ```swift extension UIColor { convenience init(hex: String) { let hexString = hex.trimmingCharacters(in: .whitespacesAndNewlines) let scanner = Scanner(string: hexString) var color: UInt64 = 0 scanner.scanHexInt64(&color) let r = CGFloat((color & 0xFF0000) >> 16) / 255.0 let g = CGFloat((color & 0x00FF00) >> 8) / 255.0 let b = CGFloat(color & 0x0000FF) / 255.0 self.init(red: r, green: g, blue: b, alpha: 1.0) } } ``` При использовании `UIColor(hex: "#FF5733")` приложение вылетает с ошибкой. Что я делаю не так? Как правильно реализовать поддержку hex-цветов в Swift? Я слышал, что в iOS 13+ есть встроенный инициализатор, но не уверен. Подскажите, пожалуйста, как сделать это правильно и безопасно. Заранее спасибо! <<>>

Правильная реализация HEX-цветов в Swift

Ваша проблема в том, что вы не удаляете символ # из строки перед сканированием. Когда Scanner встречает решётку, он не может распарсить hex-число, и возвращает 0. Вот исправленное и улучшенное решение с обработкой ошибок:

import UIKit

extension UIColor {
    /// Инициализация UIColor из hex-строки
    /// - Parameters:
    ///   - hex: Строка вида "#RRGGBB" или "RRGGBB" (регистр не важен)
    ///   - alpha: Прозрачность (0.0...1.0), по умолчанию 1.0
    convenience init?(hex: String, alpha: CGFloat = 1.0) {
        // 1. Удаляем пробелы и символ #
        var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
        hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")
        
        // 2. Проверяем длину (должна быть 6 символов для RGB)
        guard hexSanitized.count == 6 else {
            print("Ошибка: hex-строка должна содержать 6 символов (RRGGBB)")
            return nil
        }
        
        // 3. Парсим hex-строку в UInt64
        var rgbValue: UInt64 = 0
        let scanner = Scanner(string: hexSanitized)
        guard scanner.scanHexInt64(&rgbValue) else {
            print("Ошибка: не удалось распарсить hex-строку")
            return nil
        }
        
        // 4. Извлекаем компоненты RGB
        let red = CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0
        let green = CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0
        let blue = CGFloat(rgbValue & 0x0000FF) / 255.0
        
        // 5. Инициализируем UIColor
        self.init(red: red, green: green, blue: blue, alpha: alpha)
    }
}

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

// Базовое использование
let orangeColor = UIColor(hex: "#FF5733")!
let blueColor = UIColor(hex: "3366FF")!

// С прозрачностью
let semiTransparentGreen = UIColor(hex: "#00FF00", alpha: 0.5)!

// Безопасное извлечение (guard)
guard let customColor = UIColor(hex: "#AABBCC") else {
    print("Неверный hex-код")
    return
}
view.backgroundColor = customColor

Дополнительные возможности

Вы можете расширить функциональность, добавив поддержку:

  • Коротких hex-кодов (3 символа): #F00 → #FF0000
  • RGBA (8 символов): #FF573380 (последние 2 символа — alpha)
  • Статических свойств: UIColor.customOrange

Встроенное решение в iOS 13+

Начиная с iOS 13, Apple добавила удобный инициализатор UIColor(hex:) для SwiftUI. Но в UIKit вам всё равно понадобится своя реализация. Если вы используете SwiftUI, просто пишите:

import SwiftUI

struct ContentView: View {
    var body: some View {
        Color(hex: "#FF5733")
            .frame(width: 100, height: 100)
    }
}

Важно: Всегда используйте failable инициализатор (с вопросительным знаком), чтобы избежать крашей при неверном формате. И не забывайте удалять символ # перед парсингом!

<<>>

Альтернативный подход: использование UInt32 и битовых масок

Вместо строк можно работать с числовыми значениями hex-цветов. Это удобно, если вы храните цвета в конфигурационных файлах или получаете их из API в виде чисел:

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

// Использование
let redColor = UIColor(hexValue: 0xFF0000)
let purpleColor = UIColor(hexValue: 0x800080, alpha: 0.8)

Плюсы и минусы

  • + Быстрее, чем парсинг строк
  • + Нет проблем с символом #
  • - Менее читаемо, чем строковые hex-коды
  • - Требует конвертации из строки, если данные приходят в текстовом формате

Я рекомендую использовать оба подхода в вашем проекте: строковый для UI-компонентов и числовой для програмно задаваемых цветов. Главное — всегда проверяйте входные данные и используйте failable инициализаторы.