美文网首页Swift
iOS 适配暗黑模式(DarkMode)实践

iOS 适配暗黑模式(DarkMode)实践

作者: 闻人歌 | 来源:发表于2021-06-11 11:51 被阅读0次

    iOS 13 中推出了暗黑模式,可以给用户沉浸式的体验。其主要原理:

    一个资源创建两种样式。系统根据当前的UserInterfaceStyle 获取对应的样式资源;
    每次系统更新样式时,应用会调用当前所有存在的元素调用对应的一些重新方法,进行重绘视图,可以在对应的方法做相应的改动;

    UserInterfaceStyle 共有3中状态,如果不设置跟随系统,默认的初始状态是:unspecified

    @available(iOS 12.0, *)
    public enum UIUserInterfaceStyle : Int {
    
        case unspecified = 0
    
        case light = 1
    
        case dark = 2
    }
    

    系统调用更新方法,自定义重绘视图

    相关的文档

    UIView

    traitCollectionDidChange(_:)
    layoutSubviews()
    draw(_:)
    updateConstraints()
    tintColorDidChange()
    

    UIViewController

    traitCollectionDidChange(_:)
    updateViewConstraints()
    viewWillLayoutSubviews()
    viewDidLayoutSubviews()
    

    UIPresentationController

    traitCollectionDidChange(_:)
    containerViewWillLayoutSubviews()
    containerViewDidLayoutSubviews()
    

    颜色适配

    在iOS 13中提供了以下方法:

    + (UIColor *)colorWithDynamicProvider:(UIColor * (^)(UITraitCollection *))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
    - (UIColor *)initWithDynamicProvider:(UIColor * (^)(UITraitCollection *))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
    

    可以根据对应的 UserInterfaceStyle 调用不同的颜色

    我们可以根据这两个方法,创建对应的分类 UIColor+Extension.swift

    extension UIColor {
    
        static func dynamicColor(_ lightColor: UIColor, darkColor: UIColor? = UIColor.white)  -> UIColor {
            if #available(iOS 13.0, *) {
                return UIColor.init { (traitCollection) -> UIColor in
                    if traitCollection.userInterfaceStyle == .light {
                        return lightColor
                    }else{
                        return darkColor!
                    }
                }
            } else {
                return lightColor
            }
        }
    }
    

    图片适配

    打开 Assets.xcassets

    找到对应的图片

    图片

    我们在右侧工具栏中点击最后一栏,点击 Appearances 选择 Any, Dark,如图所示

    右侧工具栏

    把 DarkMode 的图片拖进去,如图所示

    dark Image

    富文本适配

    基于颜色适配的方法,对应修改

    let attrLabel = UILabel.init(frame: .init(x: 30, y: 200, width: 200, height: 30))
    view.addSubview(attrLabel)
    
    let attr = NSMutableAttributedString.init(string: "这是", attributes: [NSAttributedString.Key.foregroundColor : UIColor.dynamicColor(UIColor.red, darkColor: UIColor.orange)])
    attr.append(NSAttributedString.init(string: "富文本", attributes: [NSAttributedString.Key.foregroundColor : UIColor.dynamicColor(UIColor.black, darkColor: UIColor.white)]))
    attrLabel.attributedText = attr
    

    Light Mode

    Dark Mode

    导航栏适配

    适配返回按钮

    返回按钮使用自定义图片,对应的图片做好Dark Mode 适配.

    根据返回按钮图片设置对应的按钮 UIButton

    使用 UIBarButtonItem.init(customView: button) 设置导航栏的 leftBarButtonItem

    import UIKit
    
    class SSNavigationController: UINavigationController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
        }
    
        override func pushViewController(_ viewController: UIViewController, animated: Bool) {
            if self.viewControllers.count > 0 {
                let button = UIButton.init(type: .custom)
                button.setImage(UIImage.init(named: "back"), for: .normal)
                button.setImage(UIImage.init(named: "back"), for: .highlighted)
                button.frame = .init(x: 0, y: 0, width: 30, height: 30)
                button.addTarget(self, action: #selector(clickBack), for: .touchUpInside)
                viewController.navigationItem.leftBarButtonItem =  UIBarButtonItem.init(customView: button)
                viewController.hidesBottomBarWhenPushed = true
            }
            super.pushViewController(viewController, animated: animated)
            self.interactivePopGestureRecognizer?.delegate = nil
        }
    
        @objc
        private func clickBack() -> () {
            self.popViewController(animated: true)
        } 
    }
    

    标签栏适配

    UITabBarController 的适配,并不适用于上面的 颜色适配、图片适配,标签栏上每个 tabBarItem, 要有对应的4张图片,dark 模式下的: dark, dark_selected; light 模式下的:light,light_selected;

    import UIKit
    class SSTabBarVC: UITabBarController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
    //        let home = SSNavigationController.init(rootViewController: HomeVC())
    //        home.tabBarItem.image = UIImage.init(named: "home_light")?.withRenderingMode(.alwaysOriginal)
    //        home.tabBarItem.selectedImage = UIImage.init(named: "home_selected_light")?.withRenderingMode(.alwaysOriginal)
    //        home.title = "Home"
    ////        home.tabBarItem.imageInsets = UIEdgeInsets(top: 8, left: 0, bottom: -6, right: 0)
    //        home.tabBarItem.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.red], for: .selected)
    
    //        home.tabBarItem.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.lightGray], for: .normal)
    //        addChild(home)
    
            addChildController(viewController: HomeVC(), title: "home", imageName: "home", selectedImageName: "home_selected", textColor: UIColor.lightGray, selectedTextColor: UIColor.red, darkTextColor: UIColor.white, darkSelectedTextColor: UIColor.green)
    
            let mine = SSNavigationController.init(rootViewController: MineVC())
            mine.title = "Mine"
            addChild(mine)
    
            self.selectedIndex = 0
        }
    
        private
        func addChildController(viewController: UIViewController, title: String? = "" ,imageName: String, selectedImageName: String, textColor: UIColor? = UIColor.white, selectedTextColor: UIColor? = UIColor.black, darkTextColor: UIColor? = UIColor.white, darkSelectedTextColor: UIColor? = UIColor.black) {
            let nav = SSNavigationController.init(rootViewController: viewController)
            nav.title = title
            if #available(iOS 13.0, *), UIApplication.shared.keyWindow?.overrideUserInterfaceStyle == UIUserInterfaceStyle.dark{
                setupNaviagtionController(naviagtionController: nav, imageName: "\(imageName)_dark", selectedImageName: "\(selectedImageName)_dark", textColor: darkTextColor, selectedTextColor: darkSelectedTextColor)
            }else{
                setupNaviagtionController(naviagtionController: nav, imageName: "\(imageName)_light", selectedImageName: "\(selectedImageName)_light", textColor: textColor, selectedTextColor: selectedTextColor)
            }
    
            self.addChild(nav)
        }
    
        private
        func setupNaviagtionController(naviagtionController: UIViewController, imageName: String, selectedImageName: String, textColor: UIColor? = UIColor.white, selectedTextColor: UIColor? = UIColor.black){
            naviagtionController.tabBarItem.image = UIImage.init(named: imageName)?.withRenderingMode(.alwaysOriginal)
            naviagtionController.tabBarItem.selectedImage = UIImage.init(named: selectedImageName)?.withRenderingMode(.alwaysOriginal)
            naviagtionController.tabBarItem.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: selectedTextColor ?? UIColor.black], for: .selected)
            naviagtionController.tabBarItem.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: textColor ?? UIColor.white], for: .normal)
            naviagtionController.tabBarItem.imageInsets = UIEdgeInsets(top: 8, left: 0, bottom: -6, right: 0)
        }
    }
    

    TabBarController 动态修改颜色:

    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
            super.traitCollectionDidChange(previousTraitCollection)
    
            for nav in children {
                let index = children.firstIndex(of: nav)
                if index == 0 {
                    if #available(iOS 12.0, *) {
                        /// 先前的是 dark mode
                        if previousTraitCollection?.userInterfaceStyle == .dark {
                            setupNaviagtionController(naviagtionController: nav, imageName: "home_light", selectedImageName: "home_selected_light", textColor: UIColor.lightGray, selectedTextColor: UIColor.red)
                        }else {
                            setupNaviagtionController(naviagtionController: nav, imageName: "home_dark", selectedImageName: "home_selected_dark", textColor: UIColor.white, selectedTextColor: UIColor.green)
                        }
                    }else {
                        // Fallback on earlier versions
                    }
                }
            }
    }
    

    在这里面也尝试了使用:

    naviagtionController.tabBarItem.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: textColor ?? UIColor.white], for: .normal)
    

    来设置对应的文字颜色,但是效果并不理想,仍然会出现系统的蓝色字体
    因此,建议:tabBarItem 全部使用切图,切图中放入对应文字。

    App 内全局修改 userInterfaceStyle

    func tapForSwitch() {
        if #available(iOS 13.0, *) {
                if UIApplication.shared.keyWindow?.overrideUserInterfaceStyle == UIUserInterfaceStyle.dark {
                    UIApplication.shared.keyWindow?.overrideUserInterfaceStyle = .light
                    SSUserDefaultsConfig.currentUserInterfaceStyle = "light"
                }else if UIApplication.shared.keyWindow?.overrideUserInterfaceStyle == UIUserInterfaceStyle.light {
                    UIApplication.shared.keyWindow?.overrideUserInterfaceStyle = .dark
                    SSUserDefaultsConfig.currentUserInterfaceStyle = "dark"
                }else{
                    UIApplication.shared.keyWindow?.overrideUserInterfaceStyle = .light
                    SSUserDefaultsConfig.currentUserInterfaceStyle = "light"
                }
            } else {
    
            }
    }
    

    某个 ViewController 不遵循暗黑模式

    class HomeVC: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            view.backgroundColor = UIColor.dynamicColor(.white, darkColor: .gray)
    
            if #available(iOS 13.0, *) {
                self.overrideUserInterfaceStyle  = .light
            } else {
                // Fallback on earlier versions
            }
    
        } 
    }
    

    DarkMode跟随系统

    /// 跟随系统的dark Mode
                    window?.overrideUserInterfaceStyle = UITraitCollection.current.userInterfaceStyle
    

    GitHub

    相关文章

      网友评论

        本文标题:iOS 适配暗黑模式(DarkMode)实践

        本文链接:https://www.haomeiwen.com/subject/mznmeltx.html