Talk about recent research on dark theme adaptation for iOS (compatible with iOS 13 and below).

Apple started supporting dark mode at the system level in iOS 13, and now many apps support dark mode as well. There are plenty of mature open source implementations of Dark Mode, and there’s no reason for me to implement one myself. However, after investigating the implementation of related solutions, I found that there is still a solution that is lighter, less intrusive, more Apple style and with low learning and migration costs.

Solution overview: Native + replace Window rootViewController

The idea behind the plan is simple:

  • Generate a color using the judgment of the system version
    • iOS 13And above returns the dynamic colors supported by the systeminit(dynamicProvider: @escaping (UITraitCollection) -> UIColor).
    • Other lower-version systems use colors that correspond to themes set by the user.
  • It also determines the version when switching
    • iOS 13And later versions directly setwindowoverrideUserInterfaceStyle
    • Other lower versions initialize VC entirely and then set VC towindowrootViewController

The specific code is as follows

Set a swappable theme

In this step we set the number of topics to support

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
        }
    }
}
Copy the code

Set the color/image

class Tools {
    @UserDefaultStorage(keyName: "appTheme")
    static var _style: Int? // This is used to store the UserDefaults property globally

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

    /// Create color, the core method
    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
        }
    }

    // create img, the core method
    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
        }
    }
Copy the code

Switch the theme

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
    }
}
Copy the code

Check global Settings at system startupstyleattribute

Many products require the ability to switch between dark and day mode and not follow the system when using custom mode, so if this is required, you need to determine and set it at startup

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

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

    // If it is iOS
    if #available(iOS 13.0.*) {
        window?.overrideUserInterfaceStyle = Tools.style.mode
    }

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

    return true
}
Copy the code

Other mainstream Solutions

I have also seen some third-party libraries implemented by open source in the last few days of the subject survey. Let me give you my opinion on these libraries, which can also save people from detours in the future

RxTheme

  • A product belonging to the Rx community whose solution implementation is based onRxSwift.
  • And the way to do that is throughRxThe binding relationship of each view color is held by the view, and the theme color is changed after receiving the signal
  • Because its implementation is based on protocol, so if it is componentized development, all colors must be centrally managed in one component, and it is difficult for other components to add colors by extension.
  • The library lacks many attributes of the extension, if you encounter a need and happens to be the library does not have the attributes of the corresponding secondary extension

SwiftTheme

SwiftTheme is a very classic theme scheme written in Swift.

  • Property extensions are comprehensive, and there are few properties that the library does not take into account
  • The way to do it is rightNSObjectSpread out aDictionaryStore properties that store all controls that have been set, and then when the user triggers the switchNotification. ReceivedNotificationAfter theDictionaryEach property of each control is evaluated and reassigned
  • Because its implementation scheme is based on notification, inviewThere may be performance problems when there are many orders of magnitude

Summary of the program

Based on the comparison of the above several mainstream open source schemes, this scheme has the following advantages and disadvantages:

  • advantages

    • Low code intrusion (cost of change)
    • Performance pressure free
    • No third-party library dependencies
    • You can smoothly switch to iOS 13 in the future
    • System-level animation
  • disadvantages

    • Only dark and Bright mode is supported (other themes can be supported, but even iOS 13 + devices will need to use the replacementrootViewController“)
    • All views involving CGColor need to be implementedtraitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)methods
    • iOS 12And the following systems need to be reset when switchingrootViewController, so all the VC’s will be freed and rebuilt, i.e. the scene will be lost if other pages have logic in action

Overall, this scenario amounts to a streamlined approach to implementing the theme.

Demo

Talk is cheap, show me the code!

Demo based on the idea of this article: github.com/HanleyLee/D…

The last

The author of this article is Hanley Lee, first published in Shining Journey. If you agree with this article, please Follow