рд╕рднреА рдХреЛ рдирдорд╕реНрдХрд╛рд░!рдореЗрд░рд╛ рдирд╛рдо рдПрдВрдбреНрд░реА рд╣реИ, рдореИрдВ рдорд╛рдИ рдмреНрд░реЛрдХрд░ рдЯреАрдо рд╕реЗ рд╣реВрдВред рдореИрдВ рдЖрдкрдХреЛ рдмрддрд╛рдКрдВрдЧрд╛ .─╕ рдиреЗ iOS рдореЗрдВ рдПрдХ рдбрд╛рд░реНрдХ рдереАрдо рдХреЗ рд▓рд┐рдП рд╕рдкреЛрд░реНрдЯ рдЬреЛрдбрд╝рд╛ рд╣реИредIOS 13 рдореЗрдВ Apple рдиреЗ рдкреВрд░реЗ рд╕рд┐рд╕реНрдЯрдо рдХреЗ рд▓рд┐рдП рдПрдХ рдбрд╛рд░реНрдХ рдереАрдо рдЬреЛрдбрд╝рд╛ рд╣реИ, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ 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
}
рд╕рдВрд╕рд╛рдзрдиреЛрдВ рдореЗрдВ рдЕрдВрдзреЗрд░реЗ рд╕рдЬрд╛рд╡рдЯ рдХреЗ рд▓рд┐рдП рд░рдВрдЧ рдЬреЛрдбрд╝рдирд╛ рднреА рд╕рдВрднрд╡ рд╣реИред
рд▓реЗрдХрд┐рди рдореИрдВ рдХреЛрдб рдореЗрдВ рд░рдВрдЧ рдЬреЛрдбрд╝рдирд╛ рдкрд╕рдВрдж рдХрд░рддрд╛ рд╣реВрдВредUIColor.Palleteextension 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 Theme: Int, CaseIterable {
case light = 0
case dark
}
рд╣рдо рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рдкреБрдирд░рд╛рд░рдВрдн рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж рдЗрд╕реЗ рдкреБрдирд░реНрд╕реНрдерд╛рдкрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╡рд░реНрддрдорд╛рди рдереАрдо рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреА рдХреНрд╖рдорддрд╛ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВредextension Theme {
@Persist(key: "app_theme", defaultValue: Theme.light.rawValue)
private static var appTheme: Int
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?) {
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 }
}
}
рдПрдХ рдкреНрд░рдгрд╛рд▓реА, рдкреНрд░рдХрд╛рд╢ рдпрд╛ рдЕрдВрдзреЗрд░реЗ рд╡рд┐рд╖рдп рдЪреБрдирдиреЗ рдХреЗ рд╕реНрдХреНрд░реАрдирд╢реЙрдЯ рдкрд░рд┐рдгрд╛рдо
рдбрд╛рд░реНрдХ рдбрд┐рдЬрд╝рд╛рдЗрди рдФрд░ рд╕рд┐рд╕реНрдЯрдо, рд▓рд╛рдЗрдЯ рдФрд░ рдбрд╛рд░реНрдХ рдереАрдо рдХреЗ рдмреАрдЪ рд╕реНрд╡рд┐рдЪ рдХрд░рдиреЗ рдХрд╛ рд╕рдорд░реНрдердиредрд╕реНрдХреНрд░реАрди рд╡реАрдбрд┐рдпреЛ рдкреВрд░реЗ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдХрд╛ рд▓рд┐рдВрдХ