Swift Regex Deep Dive
iOS MacOur introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
Starting with macOS Mojave, users can choose to run a system-wide light or dark mode of the Aqua interface. While opting an application into light and/or dark appearance modes is ultimately the choice of the developer, users may come to expect that apps support both. Apple believes some content creation applications may make sense primarily as dark mode only applications.
Standard views and controls are ready for Dark Mode out-of-the-box. And if you’re already using asset catalogs and semantic colors, you’ll be able to make your app ready too with minimal code changes.
Our Astronomical app was designed using just the Aqua interface but we would like to support Dark Mode as well. We first built the app using the macOS 10.14 SDK, but it seems like we have some work to do.
Our header text was previously hardcoded to NSColor.black
but we can easily achieve the same result using the dynamic .headerTextColor
which will now do the right thing when we switch appearances.
Next, we would like to change our sun asset to a moon to represent dark mode. This can be achieved using the Asset Catalog. The Asset Catalog provides a convenient way to return a different resource using the same name that varies based on device attributes. In our case, the device attribute that is changing is the Appearance.
Note: If you are still targeting 10.13 or earlier, trying to receive a named image asset will only return the “Any Appearance” versions.
There are some cases where you may want to respond to appearance changes programmatically. You may have already noticed that Xcode 10 is nice enough to remember your Source Editor theme preference for each appearance. We would like to use this trigger to update our emoji representation of the Astronomical object we are presenting on screen.
How can we do something like this in our code? We can detect appearance changes by observing effectiveAppearance
from the newly formalized NSAppearanceCustomization
protocol. This protocol has informally existed for a while, but is now adopted by NSPopover
, NSView
, NSWindow
, and NSApplication
(new as of 10.14
).
You can use KVO to observe changes to effectiveAppearance
.
override func viewDidLoad() {
super.viewDidLoad()
appearanceChangeObservation = view.observe(.effectiveAppearance) { [weak self] _, _ in
self?.updateAppearanceRelatedChanges()
}
}
Once you detect an appearance change, you will want to figure out which appearance the application is currently in. Switching on effectiveAppearance.name
will work, but switching on bestMatch(from:)
may be a more future-proof approach. This method will attempt to provide you a good match based on the appearances you provide as the parameter.
private func updateAppearanceRelatedChanges() {
switch view.effectiveAppearance.bestMatch(from: [.aqua, .darkAqua]) {
case .aqua?: emojiField.stringValue = "☀️☀️☀️"
case .darkAqua?: emojiField.stringValue = "🌙🌙🌙"
default: emojiField.stringValue = "💫💫💫"
}
}
While our Astronomical app didn’t need to override the methods below, it is worth mentioning that these methods are the ideal place to update views when appearances change. Depending on your implementation, some of these NSView
methods will be called when an appearance changes:
updateLayer()
draw(_:)
layout()
updateConstraints()
You can trigger these methods manually by setting needsLayout
, needsDisplay
, and needsUpdateConstraints
.
We can’t wait to see how great your apps look in dark mode!
Our introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
The Combine framework in Swift is a powerful declarative API for the asynchronous processing of values over time. It takes full advantage of Swift...
Over the past several years the barrier to entry for adopting machine learning and computer vision-related features has dropped substantially. Still, many developers are...