UserInterfaceLevel, the future of floating apps?
Raul Riera
Posted on June 7, 2020
Last year Shopify started developing the All-new POS application. There was a small problem, given that all screens are presented as full screen to the user we needed the ability to distinguish a modal screen vs the rest. iOS 13 had a great answer to this.
It feels like every new iOS version adds something to UITraitCollection
or UIUserActivity
. iOS 13 was no different. There was an handy small addition to UITraitCollection
for apps with floating windows. That addition was userInterfaceLevel.
What is UserInterfaceLevel?
Apple describes it as the visual level for content in a window. The possible values are base
, elevated
, and unspecified
. The first one being the window’s main content and the last one anything above it. This will be the perfect match for our need of modal windows having a dedicated background colour.
Using it to meet our requirement
import UIKit
public struct AdaptiveColor {
/// See [UserInterfaceLevel](https://developer.apple.com/documentation/uikit/uiuserinterfacelevel) for more info.
private(set) var base: (light: UIColor, dark: UIColor)
/// The color used for content visually above your window's main content. If none is specified, the `base` color is used instead.
///
/// See [UserInterfaceLevel](https://developer.apple.com/documentation/uikit/uiuserinterfacelevel) for more info.
private(set) var elevated: (light: UIColor, dark: UIColor)
public init(light: UIColor, dark: UIColor) {
base = (light: light, dark: dark)
elevated = base
}
public init(base: (light: UIColor, dark: UIColor), elevated: (light: UIColor, dark: UIColor)) {
self.base = base
self.elevated = elevated
}
public var value: UIColor {
if #available(iOSApplicationExtension 13.0, *) {
return UIColor { traitCollection in
let isElevated = traitCollection.userInterfaceLevel == .elevated
if traitCollection.userInterfaceStyle == .dark {
return isElevated ? self.elevated.dark : self.base.dark
} else {
return isElevated ? self.elevated.light : self.base.light
}
}
} else {
return base.light
}
}
}
The previous snippet is how Shopify’s Point of Sale application is handling dynamic colours. You should focus on line #24, we detect the current elevation of the user interface and decide which of the two colour sets to use for this particular colour.
Now, let’s say we want to change the background colour of a view all we need to do is:
view.backgroundColor = AdaptiveColor(base: (light: .white, dark: .black), elevated: (light: .lightGray, dark: .gray)).value
And thanks to the excellent UIAppearance we can style our UINavigationController
similarly how we styled the background view, and all our screens will be displayed with the correct colour.
Putting everything together into a project, it looks something like this:
Further reading
That is all!, as always you can find the full source code on GitHub
raulriera / technical-articles
Just a place where I can store demo projects for my technical articles.
Posted on June 7, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.