Trying out an old iOS App of mine I discovered that some of the buttons weren’t visible in dark mode. So, in this tutorial, with a little extra time I’ve found for reasons that should be quite obvious, I’ll share how to update the UI of your app for night mode.
Step 1. Finding the problems to begin with.
If you’re like me you’re not 100% sure what dark mode is going to do to your UI. Quickly combing through my app would allow me to at least get an idea of what types of issue I should be on the lookout for. So first, I recommend switch your phone or simulator to dark mode and comparing.
So how do we quickly switch modes? Minimize app and go to settings, customizing dark mode manually every time?
No!
Toggling Between Dark and Light Mode
Thankfully, when using the simulator you can simply cmd+click this button in Xcode.(Note this only appears while the app is running from Xcode in debug mode.
By using CMD+Click we can keep our iOS simulator in the foreground while adjusting the setting, and see how each screen changes in real-time. How cool is that!?
Changing Labels and Backgrounds In Storyboards & Code
If your app is anything like mine, you’ll probably see some UILabel’s which are not colored correctly when dark mode is enabled, or you may have a UIView in the background that isn’t changing from light to dark. Let’s start with a simple UIView background that normally has a white background. In dark mode we need this to be flipped to a black background instead. This is the simplest case if you were already working with a white background in your original design. Simply change the background color to the “System Background Color” in your storyboard. If you aren’t using storyboards and need to change this programmatically, set the color to:
UIColor.systemBackground
Since UIColor.systemBackground is only existent in iOS 13 or up, you’ll need to embed this in to an availability check. That looks like this for the majority of cases when changing a white background to a system background:
BEFORE:
containerView.backgroundColor = UIColor.white
AFTER:
if #available(iOS 13.0, *) { containerView.backgroundColor = UIColor.systemBackground } else { // Fallback on earlier versions containerView.backgroundColor = UIColor.white }
This allows the background to be correctly set to your original white color in older iOS versions, but also properly sets the background color programmatically for users on iOS 13 and up.
What about controls like text views? The process is the same, except we use the “Secondary System Background Color” or the “Tertiary System Background Color” depending on the design goals of your view.
Icons and images
Now updating labels and backgrounds is fairly simple. Apple made this easy with some predefined colors. What about our icons and images that are now invisible in dark mode? What about images that just have poor contrast?
Tinting Icons and Images Programmatically
For the simple case of a solid black icon or image, we can render the image using ‘template’, and then modify the coloring of the image.
For standard UIImageView images, this takes a little additional effort. Here is an example:
In my iOS App, I have a black comment button on a white background. But when the user is in dark mode, the background is black AND the comment button is still black, rendering it invisible!
Since my icon is just a UIImageView, I can’t easily set the rendering mode. But, I can create an IBOutlet and modify the image inside its respective view controller. In my specific instance the image is nested inside of a custom UITableViewCell, so I’ll be setting the image rendering inside of layoutSubviews()
First, I’ll reset the image to use whatever I’ve specified in my original storyboard, but recreate it with the .alwaysTemplate rendering mode. After that, I can modify the color of the image by setting the tintColor variable. In my case, my icon was solid black, and I would like it to be solid white when in dark mode. This is the ‘primary content’ color used by UILabels. We can access this color using UIColor.label
In this instance, we don’t need to do anything special for older iOS versions, since it will already be black (the correct color)
commentButton.image = commentButton.image!.withRenderingMode(.alwaysTemplate) if #available(iOS 13.0, *) { commentButton.tintColor = UIColor.label } else { // Fallback on earlier versions }
By using the UIColor.label property, the color will automatically toggle between black or white, depending on the user’s settings. My icon now renders in both modes!
Tinting Image Assets
If you are using images from an asset catalog (very common for storyboards) You can also specify dark mode images, similar to how you can specify multiple resolutions for images. Start by selecting the asset in the asset library, and then under the attributes inspector note the ‘Appearances’ option. By default this will be set to ‘None’
Change this to ‘Any, Dark’ or ‘Any, Light, Dark’ and the options for the assets will expand. Here you can simply drag and drop your dark-mode image in place, and iOS will automatically use the dark mode version of the asset when appropriate.
Depending on your design, you may have items that aren’t solid black or solid white. With these, it’s best to use seperate image assets for the two different modes. You also have the option of using one of the many other included system colors. To get a close look at all the options, check out the open source SemanticUI project on github: https://github.com/aaronbrethorst/SemanticUI
With this project you can see much more of the variety of system colors available in iOS 13, and make the best choice for dark mode in your project.
And that’s essentially how you add Night Mode support. Let me know in the comments if you’ve run in to any other issues or have any questions.
By the way, if you want to learn some skills for nailing job interviews, I recently started a new site dedicated to solving the kinds of problems that interviewers ask. For example a good place to start might be this post on the top 5 google interview questions. Ok that’s all for now!