How can I reload views after choose dark mode? - ios

Im using this pod for add dark mode to my app.
https://github.com/draveness/NightNight
Its working well when I restart app again but I want to change theme inside of app. So, I added UISwitch to my sidepanel for user can change theme.
I added this codes for it and some colors changing well but some colors does not affect. For example NavigationBar background color is changing well but title color is not changing.
UISwitch Action:
#IBAction func switchMode(_ sender: UISwitch) {
if sender.isOn {
switcher.isOn = true
NightNight.theme = NightNight.Theme.night
UITabBar.appearance().barTintColor = UIColor(hexString: "#141d27")
UITabBar.appearance().isTranslucent = true
UITabBar.appearance().tintColor = UIColor(hexString: "#6e00ff")
UINavigationBar.appearance().tintColor = UIColor(hexString: "#6e00ff")
UINavigationBar.appearance().isTranslucent = true
UINavigationBar.appearance().barTintColor = UIColor(hexString: "#141d27")
for window in UIApplication.shared.windows {
for view in window.subviews {
view.removeFromSuperview()
window.addSubview(view)
}
}
UserDefaults.standard.set("night", forKey: "colormode")
} else {
switcher.isOn = false
NightNight.theme = NightNight.Theme.normal
UITabBar.appearance().barTintColor = UIColor.white
UITabBar.appearance().isTranslucent = true
UITabBar.appearance().tintColor = UIColor(hexString: "#6e00ff")
UINavigationBar.appearance().tintColor = UIColor(hexString: "#6e00ff")
UINavigationBar.appearance().isTranslucent = true
UINavigationBar.appearance().barTintColor = UIColor.white
for window in UIApplication.shared.windows {
for view in window.subviews {
view.removeFromSuperview()
window.addSubview(view)
}
}
UserDefaults.standard.set("normal", forKey: "colormode")
}
}
Normally gray text colors(User`s names and navigation title) must to be black in light mode, but they does not changed.

Use callback when view mode changes.
Change your colors as expected.
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
// do whatever you want to do
}

Check out RxSwift and RxCocoa. They can both be found here. They are Reactive Frameworks for Reactive Programming in Swift. You can create observables to change the color of views reactively, as needed.

Related

Update UI when toggle between darkmode and lightmode then enter foreground

I have a login screen which look like this:
Basically, i want to setup so that after the following actions: when in this login screen -> enter background -> toggle darkmode or lightmode in settings -> enter foreground ,the dropShadowView and its element are changed accordingly. Here down below is my code:
in viewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
setupForDarkmode()
NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
}
And function:
#objc func willEnterForeground() {
setupForDarkmode()
}
private func setupForDarkmode() {
if #available(iOS 13.0, *) {
overrideUserInterfaceStyle = .unspecified
if traitCollection.userInterfaceStyle == .dark {
dropShadowView.backgroundColor = .black
userNameTextField.backgroundColor = .black
userNameTextField.textColor = .white
passwordTextField.backgroundColor = .black
passwordTextField.textColor = .white
} else {
dropShadowView.backgroundColor = .white
userNameTextField.backgroundColor = .gray
userNameTextField.textColor = .blue
passwordTextField.backgroundColor = .gray
passwordTextField.textColor = .blue
}
}
}
screen's background was already set as system's background color.
However these above code wasn't worked as i expected. The first time it enter foreground right after the change in setting, the dropShadowView and its element keep its appearance until the second time.
After debug a while, i found that right after the change in settings (lightmode -> darkmode for exp), at line if traitCollection.userInterfaceStyle == .dark {, it wasn't recognized current userInterfaceStyle at first (unlike system's background color).
My application's target is lower than iOS 11 so i also have some prolem using colorset.
The question is is there and posible way to update the UI for darkmode programmatically when enter foreground?
Thanks in advance.
The problem is solved. I used custom color and UI was automatically update:
How do I easily support light and dark mode with a custom color used in my app?
It should look like this:
extension UIColor {
static var dropShadowViewBackground: UIColor {
if #available(iOS 13.0, *) {
return UIColor { (traits) -> UIColor in
// Return one of two colors depending on light or dark mode
return traits.userInterfaceStyle == .dark ? .black : .white
}
} else {
// Same old color used for iOS 12 and earlier
return .white
}
}
}
This work perfectly!

Alternate UIView background between two colors by pressing a button

I'm trying to set up one button that each time pressed would alternate between two colours (black and white).
Initially the app loads with a black background, with the first button tap it swaps to a white background, with the second tap it swaps back to a black background and so fourth.
I imagine it would be a simple if else statement, however I don't know how to create a variable that is the current UI background colour.
#IBAction func background_toggle(_ sender: UIButton) {
self.view.backgroundColor = UIColor.white
}
The button swaps it to white, but there isn't any implementation to swap back to black when there is a second press.
You can use backgroundColor as a condition to check which one you should pick next.
Approach using a backgroundColor attribute
#IBAction func background_toggle(_ sender: UIButton) {
if self.view.backgroundColor == .white {
self.view.backgroundColor = .black
} else {
self.view.backgroundColor = .white
}
}
In other words: If backgroundColor is .white, then set it .black
Otherwise, set it .white
Tips:
Since backgroundColor is an UIColor, Swift infers the type, so you can just type .white or .black when assigning it, without having to specify UIColor.white or UIColor.black.
You can have a state variable like a Bool, UIColor or Enum. Then you compare to that instead of the backgroundColor of the view.
Approach using a Bool variable
var isWhite = true
#objc func handleOpenTermsAndConditions() {
if self.isWhite {
self.isWhite = false
self.view.backgroundColor = .black
} else {
self.isWhite = true
self.view.backgroundColor = .white
}
}
Approach using an UIColor variable
var currentBackgroundColor : UIColor = .white
#objc func handleOpenTermsAndConditions() {
if currentBackgroundColor == .white {
self.currentBackgroundColor = .black
self.view.backgroundColor = .black
} else {
self.currentBackgroundColor = .white
self.view.backgroundColor = .white
}
}
Approach using an Enum state
If you want to restrict colors you can create an Enum with available colors. I think it's overkill to use it with some logic that is as simple as .white | .black.
Step 1: Create an enum.
enum AvailableColor {
case white
case black
func currentUIColor() -> UIColor {
switch self {
case .white:
return UIColor.white
case .black:
return UIColor.black
}
}
}
Step 2: Instantiate a state variable and use the enum logic
var currentColorState : AvailableColor = .white
#objc func handleOpenTermsAndConditions() {
if currentColorState == .white {
currentColorState = .black
} else {
currentColorState = .white
}
self.view.backgroundColor = currentColorState.currentUIColor()
}
Don't try to use the contents of the view to determine the next state by comparing the colors.
Separate out your model from your view and keep a separate property that has the current state. You could use a simple boolean flag isBlack, or you could create an enumeration:
enum BackgroundState {
case black, white
}
which has the slight advantage of being easier to expand if you ever want another color/state. (CaseIterable might be helpful then as well.)

iOS black line on navigation controller transition

When the push/pop transition is performed on my view controller(which has large title and search bar embedded in navigation item), the black line appears briefly, like this:
I've basically tried changing all the navigation bar colour related things, but nothing helped.
Any help would be appreciated :)
extension UINavigationBar {
var customStyle: NavigationBarCustomStyle {
set(style) {
switch style {
case .clear:
self.setBackgroundImage(UIImage(), for: .default)
self.shadowImage = UIImage()
self.tintColor = .white
self.isTranslucent = false
break
case .bottomLine:
self.tintColor = .gray
self.backgroundColor = .yellow
self.isTranslucent = false
break
}
}
get {
return self.customStyle
}
}
}
enum NavigationBarCustomStyle {
case clear
case bottomLine
// case white
}
at ViewController >> viewDidLoad method put below line:
self.navigationController?.navigationBar.customStyle = .clear
Try set navbar's background color to white (depend on your case) fix the problem for me, though there still another glitch but its better :)
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.view.backgroundColor = .white
}

How to definitively set UITabBar background color and tint color

I have been trying to set my UITabBar's tint color and background color for quite some time now and nothing seems to work.
So far I have tried:
tabBarController?.tabBar.backgroundColor = UIColor.orangeColor()
tabBarController?.tabBar.barTintColor = UIColor.whiteColor()
as well as:
UITabBar.appearance().tintColor = UIColor.orangeColor()
Neither of these seemed to have any effect on my tab bar. I'd also like to mention that I have the VC embedded in a navigation controller for which the global tint color that I set works perfectly fine.
If you want to set tabbar's tint and barTint color implicitly then in your Appdelegate.swift,
UITabBar.appearance().barTintColor = .orange
UITabBar.appearance().tintColor = .green
If you want to set tabbar's tint and barTint color for specific viewController then in ViewController.swift,
self.tabBarController?.tabBar.tintColor = .orange
self.tabBarController?.tabBar.barTintColor = .green
Set tab bar background color with barTintColor:
self.tabBar.barTintColor = UIColor.blueColor()
//or
UITabBar.appearance().barTintColor = UIColor.blueColor()
And for tab bar tint color:
self.tabBar.tintColor = UIColor.whiteColor() // Selected tab color
//or
UITabBar.appearance().tintColor = UIColor.whiteColor()
In similar fashion to how UINavigationBar is by default transparent on iOS 15 when there is no content behind it, the UITabBar works the same way. This might either be a nice visual refresh you get for free (since it is turned on by default once you build with Xcode 13) or it might cause a lot of issues for your app.
if #available(iOS 13.0, *) {
let tabBarAppearance: UITabBarAppearance = UITabBarAppearance()
tabBarAppearance.configureWithDefaultBackground()
tabBarAppearance.backgroundColor = UIColor.tabBarBackground
UITabBar.appearance().standardAppearance = tabBarAppearance
}
if #available(iOS 15.0, *) {
UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance
}
Also you can set it from UIEditor like so
I always like to do some kinds of settings on the storyboard. Here is the #IBDesignable extension
#IBDesignable extension UITabBar {
#IBInspectable var barTintColor: UIColor? {
set {
guard let uiColor = newValue else { return }
UITabBar.appearance().barTintColor = uiColor
}
get {
guard let color = UITabBar.appearance().barTintColor else { return nil }
return color
}
}}
Swift 4+ version
UITabBar.appearance().barTintColor = UIColor.red
UITabBar.appearance().tintColor = UIColor.white

UINavigationBar tint color does not update

I am implementing a dark mode in my app. Here is my code (that I call when the screen is double tapped):
if darkMode == false {
UINavigationBar.appearance().tintColor = UIColor(hexString: "#3A3A3A")
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName:UIColor.whiteColor()]
UIApplication.sharedApplication().statusBarStyle = UIStatusBarStyle.LightContent
} else {
UIApplication.sharedApplication().statusBarStyle = UIStatusBarStyle.Default
UINavigationBar.appearance().barTintColor = UIColor(hexString: "#FFFDF3")
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName:UIColor.blackColor()]
The only thing that updates is my status bar, but the navigation bar does update after I go into another view and return back to the main view. Why is that? Is there something I'm doing wrong?
I was just dealing with the same issue, turns out if you change appearance() proxy at runtime it doesn't have any effect. You need to change directly the properties of instances. So what you need to do is have subclassed UINavigationBarController with method where you set the colors and status bar appearance, for instance:
class ColorNavigationController: UINavigationController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
setupForColor(UIFactory.sharedInstance.tintColor) //provides default color
}
func setupForColor(color: UIColor) {
navigationBar.tintColor = color
navigationBar.titleTextAttributes = [NSForegroundColorAttributeName:UIColor.whiteColor()]
UIApplication.sharedApplication().statusBarStyle = UIStatusBarStyle.LightContent
}
}
Then when you double tap the screen:
if let colorNavigationController = self.navigationController as? ColorNavigationController {
colorNavigationController.setupForColor(UIColor.redColor) // based on your current scheme
}
Got it. You can't change appearance() at runtime, but you can just do navigationController?.navigationBar.tintColor = UIColor.redColor()

Resources