SwiftUI - How do I check to see if dark mode is enabled?
Asked Answered
K

4

1

How do I check to see if dark mode on the device is enabled. I want to check this from within a view and conditionally show or hide a shadow.

I thought I could jus get the colorScheme from the environment but I think I'm missing something.

struct FloatingAddButton : View {

    @Environment(\.colorScheme) var colorScheme 

    @Binding var openAddModal: Bool

    var body : some View {
        VStack {
            Spacer()
            HStack() {
                Spacer()
                Button(action: {

                    self.openAddModal = true

                }) {

                    ZStack {
                        Circle()
                            .foregroundColor(Color(RetroTheme.shared.appMainTint))
                            .frame(width: 50, height: 50, alignment: .center)
                        if(self.colorScheme == .light) {
                            .shadow(color: .secondary, radius: 5, x: 0, y: 0)
                        }

                        Image(systemName: "plus")
                            .foregroundColor(Color.white)
                    }

                } // End Button
            }
        }
    }

}
Karakoram answered 15/9, 2019 at 11:16 Comment(2)
As dfd pointed out below the issue was trying to use an if statement with a modifier. See the updated code in the question.Karakoram
Two things, I never saw your comment because you left the "at" (@) sign on your comment. Two years in and I'm still learning too! See my comment on my answer for what I hope you'll do. Between you and both answers this is something all can use.Verve
F
6

In my code, I have a simple View extension, that makes the code a lot more readable. With it, I can apply modifiers conditionally:

.conditionalModifier(self.colorScheme == .light, LightShadow())

The full implementation is below:

extension View {
    // If condition is met, apply modifier, otherwise, leave the view untouched
    public func conditionalModifier<T>(_ condition: Bool, _ modifier: T) -> some View where T: ViewModifier {
        Group {
            if condition {
                self.modifier(modifier)
            } else {
                self
            }
        }
    }
}
struct FloatingAddButton : View {

    @Environment(\.colorScheme) var colorScheme

    @Binding var openAddModal: Bool

    var body : some View {
        VStack {
            Spacer()
            HStack() {
                Spacer()
                Button(action: { self.openAddModal = true }) {

                    ZStack {
                        Circle()
                            .foregroundColor(Color(.red))
                            .frame(width: 50, height: 50, alignment: .center)
                            .conditionalModifier(self.colorScheme == .light, LightShadow())

                        Image(systemName: "plus")
                            .foregroundColor(Color.white)
                    }
                }

            } // End Button

        }
    }
}

struct LightShadow: ViewModifier {
    func body(content: Content) -> some View {
        content.shadow(color: .secondary, radius: 5, x: 0, y: 0)
    }
}

If you ever have a case where you want to apply different modifiers for true and false, here's another extension:

extension View {
    // Apply trueModifier if condition is met, or falseModifier if not.
    public func conditionalModifier<M1, M2>(_ condition: Bool, _ trueModifier: M1, _ falseModifier: M2) -> some View where M1: ViewModifier, M2: ViewModifier {
        Group {
            if condition {
                self.modifier(trueModifier)
            } else {
                self.modifier(falseModifier)
            }
        }
    }
}
Fermentation answered 15/9, 2019 at 12:53 Comment(3)
Much, much more elegant! It took me... a few hours... to get things working in my app that I proposed as a solution. (SwiftUI is new to us all.) As always, at least to me, you are a level or more above most of us UIKit coders (and I barely consider myself one of them). nSweet!Verve
Thanks @dfd, so kind as always. I have compiled several view extensions aimed at making the code more readable. It's in my blog, as "View Extensions for Better Code Readability". Cheers ;-)Fermentation
I check your blog regularly and already have been using one for corner radius. Didn't see this one - but it has me drilling into some by your use.Verve
M
4

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 the change of the style, here is the full detailed answer

Merriweather answered 4/9, 2020 at 0:29 Comment(0)
V
2

You are using colorScheme correctly. But it looks like you have a different issue - placing a modifier inside an if statement. I found that, unlike a View, modifiers don't work that way.

The answer is to create a custom ViewModifier. In your case I'd package everything up into one modifier like this:

struct CircleStyle: ViewModifier {
    @Environment (\.colorScheme) var colorScheme:ColorScheme

    func body(content: Content) -> some View {

    if colorScheme == .light {
        return content
            .foregroundColor(Color(RetroTheme.shared.appMainTint))
            .frame(width: 50, height: 50, alignment: .center)
            .shadow(color: .secondary, radius: 5, x: 0, y: 0)
    } else {
        return content
            .foregroundColor(Color(RetroTheme.shared.appMainTint))
            .frame(width: 50, height: 50, alignment: .center)
    }
}

And to use it:

Circle()..modifier(CircleStyle())

If you need to add more variables from your model, simply pass it into your modifier.

Verve answered 15/9, 2019 at 11:50 Comment(4)
This didn't work for me. I get a error Function declares an opaque return type, but the return statements in its body do not have matching underlying types. Thanks for the info about using if statements on modifiers. With that I was able to get it working.Karakoram
First, glad I helped. Second, good answer! Too bad it's an edit to your question? Please, post your answer as that! There's nothing wrong with that and I'd gladly upvote it. (You can't accept it - I think - but this site is really about both the Q and the A. And if I may? Edit the question title to reflect the real issue. This helps everyone!)Verve
Will do, thanks. I'm still kind of new to Stack Overflow so I don't know all of the etiquetteKarakoram
@Karakoram did you resolve that error? I'm getting the same, thanksPalsy
K
2

Thanks to @dfd for pointing out that I can't use an if statement with a modifier. I updated my code like this for now. This just returns different versions of the circle in light and dark mode.

if colorScheme == .light {
    Circle()
        .foregroundColor(Color(RetroTheme.shared.appMainTint))
        .frame(width: 50, height: 50, alignment: .center)
        .shadow(color: .secondary, radius: 5, x: 0, y: 0)
} else {
    Circle()
        .foregroundColor(Color(RetroTheme.shared.appMainTint))
        .frame(width: 50, height: 50, alignment: .center)
}
Karakoram answered 15/9, 2019 at 14:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.