Всем привет!
Меня зовут Андрей, я из команды «Мой Брокер». Я рассĸажу Вам ĸаĸ добавлял поддержĸу темной темы в iOS.
Apple в iOS 13 добавила темную тему для всей системы, пользователи могут выбрать светлое или темное оформление на настройках iOS. В темном режиме система использует более темную цветовую палитру для всех экранов, видов, меню и элементов управления.
Кому интересно — заходите под кат.
Приложение созданное в Xcode 11 по-умолчанию поддерживает темное оформление в iOS 13. Но для полноценной реализации темного режима, необходимо внести дополнительные правки:
В iOS 13 был представлен новый инициализатор UIColor:
Добавим статическую функцию для создания цвета с поддержкой переключения между светлым и темным оформлением:
CGColor не поддерживает автоматическое переключение между светлым и темным оформлением. Необходимо вручную менять CGColor после изменения оформления.
Так же есть возможность добавить цвет для темного оформления в ресурсах.
Но я предпочитаю добавлять цвета в коде.
Для изображений достаточно добавить вариант изображения для темного оформления прям в ресурсах.
Приложение будет содержать два окна и три экрана.
Первое окно: экран авторизации.
Второе окно: экран ленты и экран профиля пользователя.
Создаем enum для темы:
Добавляем возможность хранения текущей темы, чтобы восстановить её после перезапуска приложения.
Чтобы принудительно установить оформление нужно изменить стиль всех окон приложения.
Реализуем переключение темы в приложении.
Так же необходимо менять стиль окна на текущую тему перед показом окна.
Добавляем системную тему в enum темы.
После принудительной установки светлой или темной темы, нельзя определить какое оформление включено в системе. Чтобы узнавать системное оформление добавляем окно в приложение, у которого не будем принудительно менять оформление. Так же необходимо реализовать изменение оформления, когда в приложении установлена системная тема и пользователь меняет оформление в iOS.
Поддержка темного оформления и переключение между системной, светлой и темной темой.
Ссылка на весь проект
Меня зовут Андрей, я из команды «Мой Брокер». Я рассĸажу Вам ĸаĸ добавлял поддержĸу темной темы в iOS.
Apple в iOS 13 добавила темную тему для всей системы, пользователи могут выбрать светлое или темное оформление на настройках iOS. В темном режиме система использует более темную цветовую палитру для всех экранов, видов, меню и элементов управления.
Кому интересно — заходите под кат.
Поддержка темного оформления
Приложение созданное в Xcode 11 по-умолчанию поддерживает темное оформление в iOS 13. Но для полноценной реализации темного режима, необходимо внести дополнительные правки:
- Цвета должны поддерживать светлое и темное оформление
- Изображения должны поддерживать светлое и темное оформление
Apple добавила несколько системных цветов, которые поддерживают светлое и темное оформление.
В 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
}
Так же есть возможность добавить цвет для темного оформления в ресурсах.
Но я предпочитаю добавлять цвета в коде.
UIColor.Pallete
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(named: "secondaryBackground") ?? .black
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
}
}
Persist
@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 }
}
}
Скриншоты выбора системной, светлой или темной темы
Результат
Поддержка темного оформления и переключение между системной, светлой и темной темой.
Скринвидео
Ссылка на весь проект