Adding dark mode to a UIKit App Nov 4 2020

First, remember create a new branch for the changes, we can do that using Xcode or the git(1) command-line tool.

The basic support

iOS 13 introduced semantic colours, so UIKit already comes with support for automatic colour selection depending on the UserInterfaceStyle. So the simplest setup is to change our UIColor to semantic colours. For example:

1
2
3
4
5
if #available(iOS 13.0, *) {
        view.backgroundColor = UIColor.systemBackground
} else {
    view.backgroundColor = UIColor.white// Fallback on earlier versions
}

The alternative would be UIColor.secondarySystemBackground. You can check the UI Elements Colours on the Apple Documentation.

Allowing the user to set the theme

We could define themes and allow the user to choose between them. For example (Inside a ViewController):

1
2
3
4
5
func setDarkModeTheme() {
    if #available(iOS 13.0, *) {
        view.window?.overrideUserInterfaceStyle = .dark // [.dark, .light, .unspecified]
    }
}

Storing the user preferences on UserDefaults

Let's assume we are using an enum to represent the theme:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
enum ThemeStyle: Int {
    case dark = 1
    case light = 2
    case systemDefault = 3
}

extension ThemeStyle {
    var userInterfaceTheme: UIUserInterfaceStyle {
        switch self {
        case .dark:
            return .dark
        case .light:
            return .light
        case .systemDefault:
            return .unspecified
        }
    }
}

We can get and set a theme key on the user's defaults, by saving the raw value of the theme on the UserDefaults.

Reading from UserDefaults:

1
2
3
4
//Read from UserDefaults
let defaults = UserDefaults.standard
let themeRaw = defaults.integer(forKey: "MyTheme")
let theme = ThemeStyle(rawValue: themeRaw) ?? ThemeStyle.systemDefault

Notice that defaults.integer(forKey:) will return 0 if it didn't find a value for the "MyTheme" key.

Setting the value, assuming that we have the following enum for the theme style:

We can save the style in UserDefaults with the following code:

1
2
3
let defaults = UserDefaults.standard
self.theme = theme
defaults.set(self.theme.userInterfaceTheme.rawValue, forKey: "MyTheme")

Related topics/notes of interest


** If you want to check what else I'm currently doing be sure to follow me on twitter @rderik or subscribe to the newsletter. If you want to send me a direct message you can send it to derik@rderik.com.