Ability to detect device light/dark mode

I searched everything I could think of and I’ve tried a few things myself just tinkering but I cannot find a reliable way to make a conditional statement regarding system mode.

Is there no way to probe the system for which mode it’s currently in?

@Kizik - hi, not sure what you mean by system mode. Could you provide details ?

I’m curious if there is any way to determine if the device is in light or dark mode. I don’t see anything in the documentation for it.

My current workaround is using time and location data but not everyone uses dark mode to begin with so it seems more feasible to check programmatically if dark mode is on or not but I don’t see any support for that.

Unless I’m missing something.

@Kizik - Ah, I see what you mean. I can’t help you there, needs someone who is familiar with iOS and know’s where all the OS parameters are and how to access them. Need to ask @John or @sim .

Edit: thinking about this I don’t think you need to be concerned about it. If you need light or dark modes for your project just format your background and fill() accordingly - it’s in your own hands. In fact you could program to let the user choose their own background and font colours.

I want a few of my menu elements to be dependent on the system theme and I’d like for them to react dynamically. The only way I can think to do that is by probing the system to check which mode it’s in. Unfortunately haven’t yet found a way to do that.

I fully intend to include a switch to allow the user to set a preference that overrides the default values but I want the default to be “follow system theme”

@kizik - I see what you are aiming for, no fiddling around by the user when your app is run. Very professional approach. Not sure if is possible with Codea as it stands but should be if we could interrogate the iPad environment settings. Maybe
@jfperusse could help.

There you go:

function setup()
    print("current: " .. objc.viewer.traitCollection.userInterfaceStyle)
    print("dark: " .. objc.enum.UIUserInterfaceStyle.dark)
    print("light: " .. objc.enum.UIUserInterfaceStyle.light)
    print("unspecified: " .. objc.enum.UIUserInterfaceStyle.unspecified)

@jfperusse - excellent, another learning point for me and I’m sure @kizik will appreciate it. Although - it does raise the question on what we can pick up in this way from iOS. Is there a list of systems variables accessible in this way ?

The best way to determine how to do this is looking online for how to implement it in Objective-C, and then try to translate it to Codea objc.

I personally searched for “ios sdk detect light dark mode” and looked at the first result which was the following for me: swift - How to check for Dark Mode in iOS? - Stack Overflow

You can see the Swift code is relatively simple: self.traitCollection.userInterfaceStyle == .dark and that we are inside a ViewController (in Codea, the view controller can be accessed using objc.viewer).

Then inside Codea, I started typing objc.viewer.trait… and it would autocomplete, which is a good indication that we are using the right names. userInterfaceStyle would also auto-complete.

As for .dark, I know this is the enum value, so I looked up the actual enum name in Apple’s documentation and found UIUserInterfaceStyle | Apple Developer Documentation.

Knowing that enums are accessed through objc.enum, I then had everything needed for this sample.

In this case it’s a very simple scenario, but that’s the general idea how I approach these problems. Find how it’s done in Objective-C (ideally, since Swift isn’t always as easy to translate to Codea objc), then iterate on the code until it works.

@jfperusse - thanks for the reply and the explanation. I’ve had a quick look at the links you’ve provided, I need a little time to digest the info.

Thanks again

I was wondering how exactly to reverse engineer the objc frameworks.

My only issue with the provided code is the expected output.

I get the exact same response whether the system is in light or dark mode.

I am running iOS 18 beta 2 so maybe an iOS 18 issue? What’s the output from a device running < 18? And does that output change with system mode changes?

Thanks for clarification.

Seems like I am also always getting “dark”, so I’ll need to revisit this. Sorry for not testing it properly :face_with_peeking_eye:

Alright, spent some time looking into this and figured out a way which seems to work by getting the screen’s traitCollection.

Here’s how to access it (you can put the intermediate members in variables if you want the code to be cleaner ;))


1 Like

@jfperusse - wow, digging out that system variable looks like it took a lot of digging. You must know the system very well. Impressive.

This is EXACTLY what I was looking for.

1 for light 2 for dark.

Works on 18 beta 2

Don’t know how long it took you but thank you VERY much. I would have never been able to reverse engineer that out.

1 Like

Just an explanation for why you always get “dark” with the first method. Codea’s runtime view runs in “dark” mode all the time (it doesn’t support light/dark variants). It does this by overriding the UI trait collection for the view controller to be dark only

@jfperusse’s workaround of going up to the screen level bypasses this to see what the system is using at a higher level

In order to get my menu state to react to changes in realtime something like this would be necessary.

local c

function setup()
  viewer.mode = FULLSCREEN

function draw()

function checkMode(mode)
  mode = objc.viewer.view.window.screen.traitCollection.userInterfaceStyle
  if mode == 1.0 then 
    c = color(235)
    c = color(35)

Any idea what kind of impact this has?

I can simplify it by running it through an “on enter” event but then it only updates when the state changes. That’s acceptable but I’d like to avoid it if possible.

Better follow up would be. Is there a way to tell when performance is impacted other than the obvious “it’s running slow now” maybe a frame rate to delta time checker. If you get fewer frames then you know performance took a hit?

Just spitballing