Programmatically detect dark mode in SwiftUI to display appropriate Image
You can use @Environment(\.colorScheme) var colorScheme: ColorScheme
in any view to get whether the device is in dark mode (.dark
) or light mode (.light
). Using that information, you can conditionally decide which image to show easily with a ternary operator.
For example, if you have an image named "lightImage" for light mode and "darkImage" for dark mode:
@Environment(\.colorScheme) var colorScheme: ColorScheme
var body: some View {
Button(action: {
foo()
}) {
Image(colorScheme == .light ? "lightImage" : "darkImage")
}
}
SwiftUI not detecting dark mode from setting
Turns out when you use
.preferredColorScheme(.light | .dark)
It applies that preference to the entire view rather than the element the property is being used on.
How to detect if Color is white or black in Swift
You can get the HSB brightness component from Color
by first converting it to a UIColor
. Here's a little extension I made to do this:
import UIKit
import SwiftUI
extension Color {
enum Brightness {
case light, medium, dark, transparent
private enum Threshold {
static let transparent: CGFloat = 0.1
static let light: CGFloat = 0.75
static let dark: CGFloat = 0.3
}
init(brightness: CGFloat, alpha: CGFloat) {
if alpha < Threshold.transparent {
self = .transparent
} else if brightness > Threshold.light {
self = .light
} else if brightness < Threshold.dark {
self = .dark
} else {
self = .medium
}
}
}
var brightness: Brightness {
var b: CGFloat = 0
var a: CGFloat = 0
let uiColor = UIColor(self)
uiColor.getHue(nil, saturation: nil, brightness: &b, alpha: &a)
return .init(brightness: b, alpha: a)
}
}
Color.white.brightness // .light
Color.gray.brightness // .medium
Color.black.brightness // .dark
Color.clear.brightness // .transparent
The thresholds are ad hoc and untested for any real purpose. The UIColor.init
used here also requires iOS 14+ (https://developer.apple.com/documentation/uikit/uicolor/3550899-init).
How to detect Light\Dark mode change in iOS 13?
SwiftUI
With a simple environment variable on the \.colorScheme
key:
struct ContentView: View {
@Environment(\.colorScheme) var colorScheme
var body: some View {
Text(colorScheme == .dark ? "Its Dark" : "Its. not dark! (Light)")
}
}
UIKit
As it described in WWDC 2019 - Session 214 around 23:30.
As I expected, this function is getting called a lot including when colors changing. Along side with many other functions for ViewController
and presentationController
. But there is some especial function designed for that has a similar signature in all View
representers.
Take a look at this image from that session:
Gray: Calling but not good for my issue, Green: Designed for this
So I should call it and check it inside this function:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
dropShadowIfNeeded()
}
}
This will guarantee to be called just once per change.
if you are only looking for the initial state of the style, check out this answer here
Implement dark mode switch in SwiftUI App
Single View
To change the color scheme of a single view (Could be the main ContentView
of the app), you can use the following modifier:
.environment(\.colorScheme, .light) // or .dark
or
.preferredColorScheme(.dark)
Also, you can apply it to the ContentView
to make your entire app dark!
Assuming you didn't change the ContentView
name in scene delegate or @main
Entire App (Including the UIKit
parts and The SwiftUI
)
First you need to access the window to change the app colorScheme that called UserInterfaceStyle
in UIKit
.
I used this in SceneDelegate
:
private(set) static var shared: SceneDelegate?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
Self.shared = self
...
}
Then you need to bind an action to the toggle. So you need a model for it.
struct ToggleModel {
var isDark: Bool = true {
didSet {
SceneDelegate.shared?.window!.overrideUserInterfaceStyle = isDark ? .dark : .light
}
}
}
At last, you just need to toggle the switch:
struct ContentView: View {
@State var model = ToggleModel()
var body: some View {
Toggle(isOn: $model.isDark) {
Text("is Dark")
}
}
}
From the UIKit part of the app
Each UIView
has access to the window, So you can use it to set the . overrideUserInterfaceStyle
value to any scheme you need.
myView.window?.overrideUserInterfaceStyle = .dark
How to check for Dark Mode in iOS?
UIKit has had UITraitCollection for a while now. Since iOS 9 you could use UITraitCollection to see whether the device supports 3D Touch (a sad conversation for another day)
In iOS 12, UITraitCollection got a new property: var userInterfaceStyle: UIUserInterfaceStyle
which supports three cases: light
, dark
, and unspecified
Since UIViewController inherits UITraitEnvironment, you have access to the ViewController's traitCollection
. This stores userInterfaceStyle
.
UITraitEnviroment also has some nifty protocol stubs that help your code interpret when state changes happen (so when a user switches from the Dark side to the Light side or visa versa). Here's a nice coding example for you:
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if self.traitCollection.userInterfaceStyle == .dark {
// User Interface is Dark
} else {
// User Interface is Light
}
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
// Trait collection has already changed
}
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
// Trait collection will change. Use this one so you know what the state is changing to.
}
}
Related Topics
How to Resolve iOS Link Errors with Opencv
Find Favorite Contacts from the iOS Address Book API
Dequeuereusablecellwithidentifier:Forindexpath: VS Dequeuereusablecellwithidentifier:
Didreceiveremotenotification Function Doesn't Called with Fcm Notification Server
Module Compiled with Swift 5.0.1 Cannot Be Imported by the Swift 5.1 Compiler
Remove Underline on Uibutton in iOS 7
How to Separate Emojis Entered (Through Default Keyboard) on Textfield
Uilongpressgesturerecognizer on Uitableviewcell - Double Call
Crashlytics Does Not Show Crashes
How to Retrieve a File/Resource in a iOS Framework Bundle from a Main Program
Uitableview: Nested Section Headers
Uisplitviewcontroller on iPad with Storyboards
Is Float, Double, Int an Anyobject
Convert Arabic String to English Number in Swift
Memory Leak with "Libbacktracerecording.Dylib" in React Native iOS Application
Duet - Merge 2 Videos Side by Side
How to Get All Nsrange of a Particular Character in a Nsstring