Search code examples
iosswiftsfsafariviewcontrollerios-darkmode

SFSafariViewController no dark mode with custom bar tint color


I call SFSafariViewController from my app to open myurl. I changed the bar tint color of SFSafariViewController. This works fine using:

vc.preferredBarTintColor = UIColor.teal025

However when device changes appearance style mode from light to dark, the bar tint color remains teal025 and doesn't adjust to a darker color, as default bar tint color does (e.g. light gray to dark gray) if not set using preferredBarTintColor.

Major parts of the app also use teal025 as navigation bar tint color. These object adjust color automatically as desired, when entering dark mode.

How can I have the same behavior for SFSafariViewController bar tint color?

Here the entire code:

let urlString = "https://myurl"
if let url = URL(string: urlString) {
    let vc = SFSafariViewController(url: url)
    vc.preferredBarTintColor = UIColor.teal025 // this prevents dark mode change
    vc.preferredControlTintColor = UIColor.teal100
    present(vc, animated: true)
}

NB1: I don't want to use UIWebView since myurl uses Google fonts, which don't show properly on iOS using UIWebView.

NB2: teal025 and teal100 are custom colors.

--- UPDATE --- (10.01.2021)

As requested, here how I define(d) my colors.

extension UIColor {
    static var teal025: UIColor { return UIColor(red: 0.0, green: 127.0/255.0, blue: 127.0/255.0, alpha: 1.0) } // 0x008080
}

--- UPDATE --- (12.01.2021)

My aim is to have a SFSafariViewController bar tint which has exactly the same color tints, gradient and translucency than the app's navigation bar. My navigation is type Prefer Large Titles style along with scrollEdgeAppearance. These two properties handle automatic light/dark preference changes as well as translucency and vertical color gradients. I believe SFSafariViewController doesn't have provisions for all that. So the closest would be the dynamicProvider initializer of UIColor, as suggested by CSmith. This would "only" address the light/dark preference change.

let urlString = "https://myurl"
if let url = URL(string: urlString) {
    let vc = SFSafariViewController(url: url)
    vc.preferredBarTintColor = UIColor.safariBarTint
    vc.preferredControlTintColor = UIColor.safariControlTint
    present(vc, animated: true)
}

extension UIColor {
    struct Material {
        static var orangeA100: UIColor { return UIColor(red: 0xff / 0xff,
                                                        green: 0xd1 / 0xff,
                                                        blue:  0x80 / 0xff,
                                                        alpha: 1.0) } // #FFD180
        ...
        static var orangeA700: UIColor { return UIColor(red: 0xff / 0xff,
                                                        green: 0x6d / 0xff,
                                                        blue:  0x00 / 0xff,
                                                        alpha: 1.0) } // #FF6D00
    }
}

static var safariBarTint: UIColor {
    if #available(iOS 13.0, *) {
        return UIColor { (traits: UITraitCollection) -> UIColor in
            return traits.userInterfaceStyle == .dark ?
                UIColor.Material.orangeA700 :
                UIColor.Material.orangeA100
        }
    } else { // for iOS 12 and earlier
        return UIColor.Material.orangeA100
    }
}
static var safariControlTint: UIColor {
    if #available(iOS 13.0, *) {
        return UIColor { (traits: UITraitCollection) -> UIColor in
            return traits.userInterfaceStyle == .dark ?
                UIColor.Material.orangeA100 :
                UIColor.Material.orangeA700
        }
    } else { // for iOS 12 and earlier
        return UIColor.Material.orangeA700
    }
}

I believe a 1:1 color adaptation between app's navigation bar and SFSafariViewController must be done manually, hence remains a shot in the dark?!

I believe above code is the closest I can get.


Solution

  • I observe SFSafariViewController properly respecting the light and dark variants of the UIColor set as preferredBarTintColor. Your definition of teal025 in the UIColor extension defines a single color variant (0x008080) and does not have a separate dark mode color.

    To resolve define your teal025 in a Color asset catalog and load the value using:

    preferredBarTintColor = UIColor.init(named:"teal025")

    Make sure to define both dark and light variants in the color asset by selecting "Any,Dark" for the setting Appearance, then set appropriate colors for each.

    Or, use the dynamicProvider initializer of UIColor to return dark and light mode variants at run time, something like this:

    extension UIColor 
    {
        static var teal025: UIColor 
        {
            if #available(iOS 13.0, *) 
            {
                return UIColor { (traits: UITraitCollection) -> UIColor in
                    
                    // Return one of two colors depending on light or dark mode
                    return traits.userInterfaceStyle == .dark ?
                        UIColor(red: 0.0, green: 0.5, blue: 0.5, alpha: 1) :
                        UIColor(red: 0.0, green: 0.4, blue: 0.4, alpha: 1)
                }
            } 
            else 
            {
                // for iOS 12 and earlier
                return UIColor(red: 0.0, green: 0.5, blue: 0.5, alpha: 1)
            }
        }
    }
    

    Lastly, and after better understanding your question, you're possibly perceiving that SFSafariViewController bar tint color looks different from your app because of bar translucency. At present there is no means of accessing the UINavigationBar of SFSafariViewController to disable translucency.