闪耀旅途

一种简单的 iOS 暗黑模式实现方案(支持低版本)

2021-01-16  本文已影响0人  闪耀旅途

说说最近对于 iOS 系统黑暗主题适配(兼容iOS 13 以下版本)的方案研究.

himg

iOS 13 开始 Apple 在系统层面支持了黑暗模式, 现在很多 App 也都支持了黑暗模式. 也有关于黑暗模式的很多成熟的开源实现方案, 按道理我没有必要再去自己实现一套了. 但是在调查了相关方案实现后我发现还是有一种更轻量, 代码侵入更小, 更符合 Apple 风格而且学习及迁移成本都很小的方案.

方案概述: 原生 + 置换 window 的 rootViewController

这套方案的核心思想是非常简单的:

具体代码实现如下

设置可切换的主题

在这一步我们设置需要支持的主题数量

enum Theme: Int, CaseIterable {
    case none = 0
    case light = 1
    case dark = 2

    var title: String {
        switch self {
            case .none: return "Follow"
            case .light: return "Light"
            case .dark: return "Dark"
        }
    }

    @available(iOS 13.0, *)
    var mode: UIUserInterfaceStyle {
        switch self {
            case .none: return .unspecified
            case .light: return .light
            case .dark: return .dark
        }
    }
}

设置颜色/图片

class Tools {
    @UserDefaultStorage(keyName: "appTheme")
    static var _style: Int? // 此处用于全局存储 UserDefaults 属性

    static var style: Theme {
        get { return Theme(rawValue: (_style ?? 0)) ?? .dark }
        set { _style = newValue.rawValue }
    }

    /// 创造颜色, 核心方法
    static func makeColor(light: UIColor, dark: UIColor) -> UIColor {
        if #available(iOS 13.0, *) {
            return UIColor { $0.userInterfaceStyle == .light ? light : dark }
        } else {
            return Tools.style == .light ? light : dark
        }
    }

    /// 创造 img, 核心方法
    static func makeImage(light: UIImage, dark: UIImage) -> UIImage {
        if #available(iOS 13.0, *) {
            let image = UIImage()
            image.imageAsset?.register(light, with: .init(userInterfaceStyle: .light))
            image.imageAsset?.register(dark, with: .init(userInterfaceStyle: .dark))
            return image
        } else {
            return Tools.style == .light ? light : dark
        }
    }

切换主题

func changeTheme(theme: Theme) {
    Tools.style = theme
    guard let window = UIWindow.hl.getKeyWindow() else { return }
    if #available(iOS 13.0, *) {
        window.overrideUserInterfaceStyle = theme.mode
    } else {
        guard let rootVC = window.rootViewController else { return }
        let tabbar = Tools.setTabVC(withIndex: self.index)
        window.rootViewController = tabbar
    }
}

系统启动时检查全局设置的 style 属性

很多产品的要求是可以自由切换黑暗与白天模式, 在使用自定义模式的时候就不跟随系统了, 因此如果有这样的需求的话就需要在启动时进行判断并设置

func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?)
                 -> Bool {

    window = UIWindow(frame: UIScreen.main.bounds)

    // 如果是 iOS
    if #available(iOS 13.0, *) {
        window?.overrideUserInterfaceStyle = Tools.style.mode
    }

    window?.rootViewController = Tools.getTabVC(withIndex: 0)
    window?.makeKeyAndVisible()

    return true
}

其他主流方案

在主题调研的这几天内也看了一些开源实现的第三方库, 说说对于这几个库的看法吧, 也可以让以后人少走点弯路

RxTheme

SwiftTheme

SwiftTheme 是一个很经典的 Swift 语言写的主题方案了.

本方案总结

基于对上面几种开源主流方案的对比, 本方案有着以下的优缺点:

总的来说, 本方案相当于提供了一种实现主题的精简型方案.

Demo

Talk is cheap, show me the code!

基于本文思路的实现 Demo: https://github.com/HanleyLee/DarkModeDemo

上一篇下一篇

猜你喜欢

热点阅读