Меня зовут Андрей, я из команды «Мой Брокер». Я рассĸажу Вам ĸаĸ добавлял поддержĸу темной темы в iOS.
Apple в iOS 13 добавила темную тему для всей системы, пользователи могут выбрать светлое или темное оформление на настройках iOS. В темном режиме система использует более темную цветовую палитру для всех экранов, видов, меню и элементов управления.

Кому интересно — заходите под кат.
Поддержка темного оформления
Приложение созданное в Xcode 11 по-умолчанию поддерживает темное оформление в iOS 13. Но для полноценной реализации темного режима, необходимо внести дополнительные правки:
- Цвета должны поддерживать светлое и темное оформление
- Изображения должны поддерживать светлое и темное оформление
В iOS 13 был представлен новый инициализатор UIColor:
init (dynamicProvider: @escaping (UITraitCollection) -> UIColor)
Добавим статическую функцию для создания цвета с поддержкой переключения между светлым и темным оформлением:
extension UIColor { static func color(light: UIColor, dark: UIColor) -> UIColor { if #available(iOS 13, *) { return UIColor.init { traitCollection in return traitCollection.userInterfaceStyle == .dark ? dark : light } } else { return light } } }
CGColor не поддерживает автоматическое переключение между светлым и темным оформлением. Необходимо вручную менять CGColor после изменения оформления.
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) layer.borderColor = UIColor.Pallete.black.cgColor }
extension UIColor { struct Pallete { static let white = UIColor.color(light: .white, dark: .black) static let black = UIColor.color(light: .black, dark: .white) static let background = UIColor.color(light: .white, dark: .hex("1b1b1d")) static let secondaryBackground = UIColor.color(light: .hex("e1e1e1"), dark: .hex("232323")) static let gray = UIColor.color(light: .lightGray, dark: .hex("8e8e92")) } }
Для изображений достаточно добавить вариант изображения для темного оформления прям в ресурсах.

Сделаем небольшое приложение для примера.
Приложение будет содержать два окна и три экрана.
Первое окно: экран авторизации.
Второе окно: экран ленты и экран профиля пользователя.

Переключение светлой и темной темы
Создаем enum для темы:
enum Theme: Int, CaseIterable { case light = 0 case dark }
Добавляем возможность хранения текущей темы, чтобы восстановить её после перезапуска приложения.
extension Theme { // Обертка для UserDefaults @Persist(key: "app_theme", defaultValue: Theme.light.rawValue) private static var appTheme: Int // Сохранение темы в UserDefaults func save() { Theme.appTheme = self.rawValue } // Текущая тема приложения static var current: Theme { Theme(rawValue: appTheme) ?? .light } }
@propertyWrapper struct Persist<T> { let key: String let defaultValue: T var wrappedValue: T { get { UserDefaults.standard.object(forKey: key) as? T ?? defaultValue } set { UserDefaults.standard.set(newValue, forKey: key) } } init(key: String, defaultValue: T) { self.key = key self.defaultValue = defaultValue } }
Чтобы принудительно установить оформление нужно изменить стиль всех окон приложения.
Реализуем переключение темы в приложении.
extension Theme { @available(iOS 13.0, *) var userInterfaceStyle: UIUserInterfaceStyle { switch self { case .light: return .light case .dark: return .dark } } func setActive() { // Сохраняем активную тему save() guard #available(iOS 13.0, *) else { return } // Устанавливаем активную тему для всех окон приложения UIApplication.shared.windows .forEach { $0.overrideUserInterfaceStyle = userInterfaceStyle } } }
Так же необходимо менять стиль окна на текущую тему перед показом окна.
extension UIWindow { // Устанавливаем текущую тему для окна // Необходимо вызывать перед показом окна func initTheme() { guard #available(iOS 13.0, *) else { return } overrideUserInterfaceStyle = Theme.current.userInterfaceStyle } }
Добавляем переключение на системной тему
Добавляем системную тему в enum темы.
enum Theme: Int, CaseIterable { case system = 0 case light case dark }
После принудительной установки светлой или темной темы, нельзя определить какое оформление включено в системе. Чтобы узнавать системное оформление добавляем окно в приложение, у которого не будем принудительно менять оформление. Так же необходимо реализовать изменение оформления, когда в приложении установлена системная тема и пользователь меняет оформление в iOS.
final class ThemeWindow: UIWindow { override public func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { // Если текущая тема системная и поменяли оформление в iOS, опять меняем тему на системную. // Например: Пользователь поменял светлое оформление на темное. if Theme.current == .system { Theme.system.setActive() } } } let themeWindow = ThemeWindow() class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { ... // Добавляем окно к приложению, но не показываем его // Необходимо вызывать до установки главного окна приложения themeWindow.makeKey() ... return true } } extension Theme { @available(iOS 13.0, *) var userInterfaceStyle: UIUserInterfaceStyle { switch self { case .light: return .light case .dark: return .dark case .system: return themeWindow.traitCollection.userInterfaceStyle } } func setActive() { // Сохраняем активную тему save() guard #available(iOS 13.0, *) else { return } // Устанавливаем активную тему для всех окон приложения // Не красим это окно чтобы узнавать системную тему UIApplication.shared.windows .filter { $0 != themeWindow } .forEach { $0.overrideUserInterfaceStyle = userInterfaceStyle } } }
Результат
Поддержка темного оформления и переключение между системной, светлой и темной темой.
ссылка на оригинал статьи https://habr.com/ru/company/bcs_company/blog/493096/
Добавить комментарий