Все это касается и используемых в приложении цветов.
Последняя редакция – 16 февраля 2019 г.
Цветовые константы
Для начала хочется дать несколько более или менее стандартных рекомендаций.
Во-первых, цвета всех элементов лучше сразу задавать в коде, а не в «Storyboard»/«Interface Builder». Если, конечно, это не приложение с одним экраном с тремя элементами и в одной цветовой схеме. Но даже и в этом случае никогда не знаешь наверняка, как изменится ситуация в будущем.
Во-вторых, все цвета стоит определить константами, вынесенными в отдельный файл. Или в отдельную группу по соседству с соответствующим «view»-кодом.
В-третьих, цвета стоит разделять на категории. Т.е. оперировать не «цветом второй кнопки на первом экране», а чем-нибудь вроде «цвета фона основного типа кнопок».
Если от дизайнера (или от собственного чувства вкуса) поступит сигнал изменить цвет какого-либо элемента, его не придется долго искать – раз, изменять в нескольких местах (забывая какое-то из них и хватаясь за голову после отправки приложения в «iTunes Connect») – два.
Таким образом мы будем иметь, например, файл
Colors.swift с содержимым вроде:import UIKit enum ButtonAppearance { static let backgroundColor = UIColor.white static let borderColor = UIColor.gray static let textColor = UIColor.black }
(
enum без единого case – честно говоря, моя любимая структура типа для объявления констант. При таком использользовании, она автоматически лишает нас возможности создавать экземпляры типа и вообще использовать тип как-либо еще кроме задуманного способа.)Использование цвета будет выглядеть так:
let someButton = UIButton() someButton.backgroundColor = ButtonAppearance.backgroundColor someButton.layer.borderColor = ButtonAppearance.borderColor.cgColor
Модель цвета
Предлагаю пойти дальше и написать модель, которая будет представлять различные используемые в приложении цвета:
struct SchemeColor { private let color: UIColor func uiColor() -> UIColor { return color } func cgColor() -> CGColor { return color.cgColor } }
Для удобства создания можно даже написать
extension для UIColor:extension UIColor { func schemeColor() -> SchemeColor { return SchemeColor(color: self) } }
В этом случае константы будут выглядеть так:
enum ButtonAppearance { static let backgroundColor = UIColor.white.schemeColor() static let borderColor = UIColor.gray.schemeColor() static let textColor = UIColor.black.schemeColor() }
В коде цвет задаваться будет таким образом:
let someButton = UIButton() someButton.backgroundColor = ButtonAppearance.backgroundColor.uiColor() someButton.layer.borderColor = ButtonAppearance.borderColor.cgColor()
И, наконец, для чего могут понадобиться такие дополнительные сложности – это…
Цветовые схемы
Допустим, мы хотим, чтобы наше приложение имело две цветовые схемы: темную и светлую. Для хранения списка цветовых схем определим
enum:enum ColorSchemeOption { case dark case light }
В данном случае, думаю, не будет зазорно создать тип для представления модели цветовой схемы в виде «синглтона»:
struct ColorScheme { static let shared = ColorScheme() private (set) var schemeOption: ColorSchemeOption private init() { /* Здесь должен быть код, который определит цветовую схему и присвоит нужное значение option. Например, загрузив настройки из UserDefaults или взяв значение по умолчанию, если сохраненных настроек нет. */ } }
Я бы его даже определил внутри
SchemeColor и сделал его private.Сам
SchemeColor нужно модифицировать для того, чтобы он был осведомлен о том, какую цветовую схему использует приложение и возвращал нужный цвет:struct SchemeColor { let dark: UIColor let light: UIColor func uiColor() -> UIColor { return colorWith(scheme: ColorScheme.shared.schemeOption) } func cgColor() -> CGColor { return uiColor().cgColor } private func colorWith(scheme: ColorSchemeOption) -> UIColor { switch scheme { case .dark: return dark case .light: return light } } // ColorScheme }
Цветовые константы теперь будут выглядеть уже так:
enum ButtonAppearance { static let backgroundColor = SchemeColor(dark: Dark.backgroundColor, light: Light.backgroundColor) static let borderColor = SchemeColor(dark: Dark.borderColor, light: Light.borderColor) static let textColor = SchemeColor(dark: Dark.textColor, light: Light.textColor) private enum Light { static let backgroundColor = UIColor.white static let borderColor = UIColor.gray static let textColor = UIColor.black } private enum Dark { static let backgroundColor = UIColor.lightGray static let borderColor = UIColor.gray static let textColor = UIColor.black } }
(
extension для UIColor, кажется, больше не нужен.)А использование всего этого добра будет выглядеть все так же:
let someButton = UIButton() someButton.backgroundColor = ButtonAppearance.backgroundColor.uiColor() someButton.layer.borderColor = ButtonAppearance.borderColor.cgColor()
Чтобы поменять цвет какого-то элемента, по прежнему хватит только изменения соответствующей константы. А чтобы добавить еще одну цветовую схему, нужно добавить
case в ColorSchemeOption, набор цветов для этой цветовой схемы в цветовые константы и обновить SchemeColor.Последнее, конечно, можно еще улучшить. Например, если количество схем разрастается, вероятно, удобней будет заменить громоздкий инициализатор на «строителя».
Пожалуй, на этот раз все! Красивого кода!

