Adicionar um tema sombrio no iOS

Olá a todos!

Meu nome é Andrey, sou da equipe My Broker. Vou dizer que você adicionou suporte para um tema sombrio no iOS.

A Apple no iOS 13 adicionou um tema sombrio para todo o sistema, os usuários podem escolher uma aparência clara ou escura nas configurações do iOS. No modo escuro, o sistema usa uma paleta de cores mais escura para todas as telas, visualizações, menus e controles.

imagem

Quem se importa - vá para baixo do gato.

Suporte de design escuro


O aplicativo criado no Xcode 11, por padrão, suporta o design escuro no iOS 13. Mas, para a implementação completa do modo escuro, você precisa fazer alterações adicionais:

  • As cores devem suportar design claro e escuro
  • As imagens devem suportar design claro e escuro

A Apple adicionou várias cores do sistema que suportam design claro e escuro.
imagem

No iOS 13, o novo inicializador UIColor foi introduzido :

init (dynamicProvider: @escaping (UITraitCollection) -> UIColor)

Adicione uma função estática para criar cores com suporte para alternar entre design claro e escuro:

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
        }
    }
}

O CGColor não suporta alternância automática entre claro e escuro. Você deve alterar manualmente o CGColor após alterar o design.

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
        
    layer.borderColor = UIColor.Pallete.black.cgColor
}

Também é possível adicionar cores para decoração escura nos recursos.

imagem

Mas eu prefiro adicionar cores no código.

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"))

    }
}


Para imagens, basta adicionar uma variante da imagem para um design escuro diretamente nos recursos.

imagem

Vamos fazer um pequeno aplicativo como exemplo.


O aplicativo conterá duas janelas e três telas.

Primeira janela: tela de autorização.

Segunda janela: tela da faixa de opções e tela de perfil do usuário.

Capturas de tela em design claro e escuro
imagem imagem imagem
imagem imagem imagem

Alternar temas claros e escuros


Crie uma enumeração para o tópico:


enum Theme: Int, CaseIterable {
    case light = 0
    case dark
}

Adicionamos a capacidade de armazenar o tema atual para restaurá-lo após reiniciar o aplicativo.

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
    }
}

Persistir
@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
    }
}


Para forçar o design, você precisa alterar o estilo de todas as janelas do aplicativo.

Implementamos a troca de temas no aplicativo.


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 }
    }
}

Também é necessário alterar o estilo da janela para o tema atual antes de mostrar a janela.

extension UIWindow {
    
    //     
    //     
    func initTheme() {
        guard #available(iOS 13.0, *) else { return }
        
        overrideUserInterfaceStyle = Theme.current.userInterfaceStyle
    }
}

Capturas de tela da escolha de um tema claro ou escuro
imagem imagem

Adicionar uma opção ao tema do sistema


Adicione um tema do sistema ao tema da enumeração.
enum Theme: Int, CaseIterable {
    case system = 0
    case light
    case dark
}

Após a instalação forçada de um tema claro ou escuro, é impossível determinar qual design está incluído no sistema. Para reconhecer o design do sistema, adicionamos uma janela ao aplicativo, na qual não forçaremos a alteração do design. Também é necessário implementar uma alteração de design quando o aplicativo tiver um tema do sistema instalado e o usuário alterar o design no 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 }
    }
}

Capturas de tela da escolha de um sistema, tema claro ou escuro
imagem imagem

Resultado


Suporte para design escuro e alternar entre temas do sistema, claros e escuros.
Vídeo em tela


Link para todo o projeto

All Articles