75

Starting from iOS/iPadOS 13, a dark user interface style is available, similar to the dark mode introduced in macOS Mojave. How can I check whether the user has enabled the system-wide dark mode?

Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
  • 2
    This user interface has actually been available since tvOS 10 and iOS 12 — on iOS 12 it was just available as "invert colors" in the accessibility options – Aaron Brager Jun 05 '19 at 02:46
  • 1
    The comment by Aaron Brager is somewhat inaccurate - yes you can "invert colors" but it's very different from turning on dark mode. It may give a false impression of your app still being usable. eg: if you inadvertently mixed system colors with your own, then invert inverts all of them. However, on dark mode, the system colors will change but yours don't. So, like Touchgram v1.1.0 you can end up with near-white text on a very pale blue background. App Store review does NOT pick this up! – Andy Dent Mar 28 '20 at 04:24
  • Here is **[To check the current state](https://stackoverflow.com/a/63733324/5623035)** and this is for **[Observing for live changes of the state](https://stackoverflow.com/a/58017164/5623035)**. Both answers cover UIKit/AppKit/SwiftUI and etc. – Mojtaba Hosseini Sep 04 '20 at 01:01

16 Answers16

79

For iOS 13, you can use this property to check if current style is dark mode or not:

if #available(iOS 13.0, *) {
    if UITraitCollection.current.userInterfaceStyle == .dark {
        print("Dark mode")
    }
    else {
        print("Light mode")
    }
}
huynguyen
  • 7,616
  • 5
  • 35
  • 48
  • 7
    This is great if you want to check in AppDelegate, since it does not have a `traitCollection` variable like `UIViewController` – Ross Sullivan Jan 12 '20 at 20:37
  • 2
    This doesn't react on disabled dark mode. If you use `window?.overrideUserInterfaceStyle = .light` then `UITraitCollection.current.userInterfaceStyle` can return `.dark`. – Valentin Shamardin Aug 03 '20 at 09:06
  • This not work in my case. Make my iOS app to Mac supported. My mac mini current Appearance is Dark, in appdelegate file i sometime get light and sometime get dark mode, not getting every time dark mode. Please help me. – Yogendra Patel Dec 29 '20 at 12:40
47

You should check the userInterfaceStyle variable of UITraitCollection, same as on tvOS and macOS.

switch traitCollection.userInterfaceStyle {
case .light: //light mode
case .dark: //dark mode
case .unspecified: //the user interface style is not specified
}

You should use the traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) function of UIView/UIViewController to detect changes in the interface environment (including changes in the user interface style).

From Apple Developer Documentation:

The system calls this method when the iOS interface environment changes. Implement this method in view controllers and views, according to your app’s needs, to respond to such changes. For example, you might adjust the layout of the subviews of a view controller when an iPhone is rotated from portrait to landscape orientation. The default implementation of this method is empty.

System default UI elements (such as UITabBar or UISearchBar) automatically adapt to the new user interface style.

Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
  • 3
    In addition to `traitCollectionDidChange(_:)`, you can alternatively check for the change in `UIView`'s `layoutSubviews()`, `draw(_:)`, `updateConstraints()`, or `tintColorDidChange()`, or in `UIViewController`'s `updateViewConstraints()`, `viewWillLayoutSubviews()`, or `viewDidLayoutSubviews()`. All of these methods are called every time the user interface style changes. – Aaron Brager Jun 05 '19 at 02:48
  • 4
    This will give you the current view's user interface style. If you've overriden it for that particular view, it won't tell you the system's style. – davextreme Sep 05 '19 at 22:03
37

As mentioned by daveextreme, checking the current view user interface style doesn't always return the system style when you use the overrideUserInterfaceStyle property. In such cases it may be better to use the following code:

switch UIScreen.main.traitCollection.userInterfaceStyle {
case .light: //light mode
case .dark: //dark mode
case .unspecified: //the user interface style is not specified
}
Ely
  • 8,259
  • 1
  • 54
  • 67
  • 2
    This answer is the best fit for checking the theme within a non UIViewController class. – Kozmotronik Sep 24 '21 at 09:13
  • Helped me. Wanted to switch between system, forced light, forced dark. Forcing a particular style, made it independent from system settings. – Mark Jul 31 '22 at 04:35
23

in objective-c you'd want to do:

if( self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark ){

        //is dark
}else{

    //is light

}
jbiser361
  • 949
  • 7
  • 20
18

SwiftUI

With the \.colorScheme key of an Environment variable:

struct ContentView: View {
    @Environment(\.colorScheme) var colorScheme

    var body: some View {
        Text(colorScheme == .dark ? "In dark mode" : "In light mode")
    }
}

Also, it automatically updates on the change of the environment color scheme.


UIKit

To check the current, all object those conform to UITraitEnvironment protocol, including all UIView subclasses and all UIViewConttroller subclasses have access to the current style:

myUIView.traitCollection.userInterfaceStyle == .dark
myUIViewController.traitCollection.userInterfaceStyle == .dark

To detect live changes of the style, here is the full detailed answer

Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
  • I used this method in my ContentView to change the accent color of a Tab Bar interface. At the end of the Tab View I used .accentColor(colorScheme == .dark ? .green : .black) – Dave Levy Sep 23 '20 at 21:46
  • I get `Type annotation missing in pattern` when declaring @Enironement like that. – Ishmael7 Jan 13 '21 at 14:52
  • DOes it check works if I have the iOS version below 13.0? – sejn Apr 26 '23 at 11:59
  • As long as I know, iOS had no dark mode before iOS 13. So it doesn't make any sense @sejn. but you **can** use this code inside a legacy project if it is your question – Mojtaba Hosseini Apr 29 '23 at 08:57
16

For Swift:

if #available(iOS 12.0, *) {
  switch UIScreen.main.traitCollection.userInterfaceStyle {
    case .dark: // put your dark mode code here
    case .light: 
    case .unspecified: 
  }
}

For Objective C:

if (@available(iOS 12.0, *)) {
        switch (UIScreen.mainScreen.traitCollection.userInterfaceStyle) {
            case UIUserInterfaceStyleDark:
                // put your dark mode code here
                break;
            case UIUserInterfaceStyleLight:
            case UIUserInterfaceStyleUnspecified:
                break;
            default:
                break;
        }
}

For more information watch this WWDC2019 video

Pedro Trujillo
  • 1,559
  • 18
  • 19
  • 3
    I ended up using it for Xamarin.Forms in my iOS project. By far the best answer out there. (good informed solid answer working better than the 100th copy by some blogger of the official doku at MS for Xamarin.) @Pedro Trujillo you saved my day. Thanks! – Ranndom Jan 30 '20 at 07:23
  • any. ios 11 version? – famfamfam Apr 02 '21 at 10:49
15

1/ for UIView/UIViewController:

self.traitCollection.userInterfaceStyle == .dark

2/ for static or other:

UITraitCollection.current.userInterfaceStyle == .dark

BUT:

//Never use this! You will get wrong value in app extensions (ex. ToDay widget)
UIScreen.main.traitCollection.userInterfaceStyle == .dark //WRONG!
Yanis
  • 167
  • 1
  • 6
  • 2
    Actually the last one (UIScreen...) was the only way to get the user's dark mode setting in the device's settings after overriding the userInterfaceStyle in my app. This way I was able to implement a "follow iOS dark mode" button which immediately updates the app's color theme even though I have custom themes and selection beside that. Unfortunately, reliably managing the status bar text color individually is impossible without overriding the userInterfaceStyle. – nontomatic Oct 23 '19 at 08:22
  • 4
    Note that may help somebody: if you have `UIUserInterfaceStyle` set to `light` in your `info.plist`, this methods will always return `light` – Leonid Silver Jul 12 '20 at 20:45
  • For me, using a keyboard extension, `UITraitCollection.current.userInterfaceStyle` returns the correct value when the extension launches, but then never changes when I toggle light and dark mode on and off. – Daniel Saidi Jul 17 '21 at 14:23
11

Create a class function for write method 1 time and use everywhere you want

func isDarkMode() -> Bool{
    if #available(iOS 12.0, *) {
        if UIScreen.main.traitCollection.userInterfaceStyle == .dark {
            return true
        } else {
            return false
        }
    } else {
       return false
    }
}  
Olcay Ertaş
  • 5,987
  • 8
  • 76
  • 112
Raza Baloch
  • 334
  • 4
  • 9
8

Objective C

To detect when dark mode is enabled or disabled via the Control Centre use an "appDidBecomeActive" notification that will be triggered when you return to your app.

//----------------------------------------------------------------------------
//                          viewWillAppear
//----------------------------------------------------------------------------
- (void)viewWillAppear {
    [super viewWillAppear];

    [[NSNotificationCenter defaultCenter]addObserver:self
                                   selector:@selector(appDidBecomeActive:)
                                   name:UIApplicationDidBecomeActiveNotification
                                   object:nil];

}

Don't forget to remove it when you're finished:

//------------------------------------------------------------------------------------
//                    viewWillDisappear
//------------------------------------------------------------------------------------
- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    [[NSNotificationCenter defaultCenter] removeObserver:self        
                                 name:UIApplicationDidBecomeActiveNotification 
                                 object:nil];

}

Do what ever you need to when dark mode changes:

//----------------------------------------------------------------------------
//                          appDidBecomeActive
//----------------------------------------------------------------------------
-(void)appDidBecomeActive:(NSNotification*)note {
    if (@available(iOS 13.0, *)) {
        if( self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark ){
            //dark mode
        }
        else {
            //not dark mode
        }
    }
    else {
        //fall back for older versions
    }
}
birdman
  • 1,134
  • 13
  • 13
7

Helper method below that works on any iOS version:

var isDarkMode: Bool {
    guard #available(iOS 12.0, *) else {
        return false
    }

    return UIScreen.main.traitCollection.userInterfaceStyle == .dark
}

Usage:

view.backgroundColor = isDarkMode ? .black : .white
Lucas Chwe
  • 2,578
  • 27
  • 17
  • 1
    Welcome to Stack Overflow. Code dumps without any explanation are rarely helpful. Stack Overflow is about learning, not providing snippets to blindly copy and paste. Please [edit] your question and explain how it works better than what the OP provided. – ChrisGPT was on strike May 14 '20 at 00:37
  • 2
    @Chris. Thanks for the comment but literally every answer here is a code dump... The question is very straight forward and so is the answer. Thanks for your pointing that out tho, ill add more explanation – Lucas Chwe May 14 '20 at 12:05
  • That doesn't make code-only answers acceptable. This comment was flagged as either a "low quality answer", probably because it was purely code, or a "late response" (I can't remember which) and I commented via the review queue. It's always best to highlight what you changed, why you changed it, and how your answer improves upon any existing ones. I count _ten_ answers that predate yours. Make yours stand out. – ChrisGPT was on strike May 14 '20 at 12:20
4

The best point to detect changes is traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) function of UIView/UIViewController.

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)

    let userInterfaceStyle = traitCollection.userInterfaceStyle // Either .unspecified, .light, or .dark
    // Update your user interface based on the appearance
}

Detecting appearance changes is trivial by overriding traitCollectionDidChange on view controllers. Then, just access the view controller’s traitCollection.userInterfaceStyle.

However, it is important to remember that traitCollectionDidChange may be called for other trait changes, such as the device rotating. You can check if the current appearance is different using this new method:

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)

    let hasUserInterfaceStyleChanged = previousTraitCollection.hasDifferentColorAppearance(comparedTo: traitCollection) // Bool
    // Update your user interface based on the appearance
}
93sauu
  • 3,770
  • 3
  • 27
  • 43
2
var isDarkMode: Bool {
    guard #available(iOS 12.0, *) else {
        return false
    }
    let window = (UIApplication.shared.delegate as? AppDelegate)?.window
    return window?.traitCollection.userInterfaceStyle == .dark
}

if you are not using window in AppDelegate, call window from SceneDelegate

It is similar to most answers above, but this works better when we are changing modes using

window?.overrideUserInterfaceStyle = .dark

can be called as

isDarkMode ? .black : .white
Bibin Alex
  • 23
  • 4
1

You can use this extension:

import UIKit

extension UIApplication {
    @available(iOS 13.0, *)
    var userInterfaceStyle: UIUserInterfaceStyle? {
        return self.keyWindow?.traitCollection.userInterfaceStyle
    }
}

@available(iOS 13.0, *)
    func setSystemTheme() {
        switch UIApplication.shared.userInterfaceStyle {
        case .dark?:
            currentTheme = .dark
        case .light?:
            currentTheme = .light
        default:
            break
        }
    }
Eugene Lezov
  • 722
  • 7
  • 12
0

Some nice extension maybe ?

public extension UIViewController {
    @available(iOS 12.0, *)
    public var isDarkMode: Bool { traitCollection.userInterfaceStyle == .dark }
}
Renetik
  • 5,887
  • 1
  • 47
  • 66
0

You can Easily Detect Dark Mode or Light Mode with this Method Swift 5

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if traitCollection.userInterfaceStyle == .light {
    print("Light mode")
} else {
    print("Dark mode")
}}
Imran Rasheed
  • 825
  • 10
  • 25
0

You can use the following code to check for light, or dark mode in your project:

func viewDidLoad() {
    super.viewDidLoad()

    switch traitCollection.userInterfaceStyle {
        case .light, .unspecified:
            // light mode detected
        case .dark:
            // dark mode detected
    }
}

You can also check for changes in the interface style:

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)

    let userInterfaceStyle = traitCollection.userInterfaceStyle // Either .unspecified, .light, or .dark
    // Update your user interface based on the appearance
}

Just like in macOS since Mojave, you can define images for both light and dark mode in your asset catalog so that those images will be used automatically:

Muhammad Ahmad
  • 388
  • 4
  • 9