UINavigationBar titleTextAttributes does not update fully after reloading views - swift

This is a tricky one.
Here is my storyboard for this demo:
The Settings screen segues to the My Color screen where users can choose either a dark or light color scheme for the app. When a change is made, I remove all views from the window and then re-add them to force the current view to immediately apply the changes via the UIAppearance proxy. So the color of the navigation bar and the nav bar's text color both change immediately.
Next, the user unwinds the segue to return to the Settings screen. On the Settings screen, the new color of the navigation bar is already applied. The new color of the nav bar's text is also already applied. However, for a brief instant while the segue is in transition, the nav bar still shows the old text color. The new text color is not shown until after the transition is complete. This results in a minor, but noticable, visual glitch as the nav bar's text suddenly changes from the old color to the new color.
To update the color of the nav bar text when the user flips the switch, I run the following code in the My Color screen's view controller. (The full project code up on Github at https://github.com/prinomen/social_demo2).
func switchValueDidChange(sender:UISwitch!) {
if (sender.on == true) {
colorIndex = 1 // nav bar is now black
UINavigationBar.appearance().barTintColor = black // set appearance proxy to the new color
// Run this switch to set the textColor global var to match the preferred color scheme, based on the value of colorIndex.
switch colorIndex {
case 0: // white
textColor = green
statusBarTextIsBlack = true
case 1: // black
textColor = red
statusBarTextIsBlack = false
default:
break;
}
// Update these appearance proxy items (they need the window to reload before they will manifest their changes).
UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName: UIFont(name: "Avenir-Medium", size: 22)!, NSForegroundColorAttributeName: textColor]
UINavigationBar.appearance().tintColor = textColor
// Remove all views from the window and then re-add them in order to force the current view to immediately apply changes to UIAppearance.
let windows : NSArray = UIApplication.sharedApplication().windows
for window in windows as! [UIWindow] {
for view in window.subviews {
view.removeFromSuperview()
window.addSubview(view)
}
}
} else {
colorIndex = 0 // nav bar is now white
UINavigationBar.appearance().barTintColor = white // set appearance proxy to the preferred color
// Run this switch to set the textColor global var to match the preferred color scheme, based on the value of colorIndex.
switch colorIndex {
case 0: // white
textColor = green
statusBarTextIsBlack = true
case 1: // black
textColor = red
statusBarTextIsBlack = false
default:
break;
}
// Update these appearance proxy items (they need the window to reload before they will manifest their changes).
UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName: UIFont(name: "Avenir-Medium", size: 22)!, NSForegroundColorAttributeName: textColor]
UINavigationBar.appearance().tintColor = textColor
// Remove all views from the window and then re-add them in order to force the current view to immediately apply changes to UIAppearance.
let windows : NSArray = UIApplication.sharedApplication().windows
for window in windows as! [UIWindow] {
for view in window.subviews {
view.removeFromSuperview()
window.addSubview(view)
}
}
}
}
Aside from changing the color via the appearance proxy, I've also tried setting the color explicitly within the viewWillAppear and viewWillLayoutSubviews methods of the Settings screen view controller by running this line:
self.navigationController?.navigationBar.titleTextAttributes = [NSFontAttributeName: UIFont(name: "Avenir-Medium", size: 22)!, NSForegroundColorAttributeName: textColor]
But this results in the same issue. What I find confusing is that the other changes made via the appearance proxy are updated without encountering this issue. Only the titleTextAttributes property is troubled by this issue.
I thought that maybe iOS makes some kind of "snapshot" of the Settings screen when segueing to the My Color screen. Then when the segue is reversed, the "snapshot" with the old nav bar text color is used and the new color is not updated until the segue is finished. But if that were true, then why doesn't the navigation bar's barTintColor also experience the same problem? There must be a different way the reverse segue is handled, but I can't seem to figure it out.
Is there a way to apply the color change to the title text before the transition happens, in a way that affects the transition itself?
Thanks for any insight!

Related

How can I make the UISearchController SearchBar background color the true, untinted color?

I tried virtually every solution I could find for changing the background color of a UISearchController SearchBar, but none of them produced the correct color as the background. Every solution produces a somewhat darker color, and as demonstrated in the image below, white seems more pale / off-white.
How can I make the search bar a true white color?
One of the more recent "solutions" that results in white being pale is below:
let searchController = UISearchController()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.searchController = searchController
let textFieldInsideSearchBar = searchController.searchBar.value(forKey: "searchField") as? UITextField
textFieldInsideSearchBar?.backgroundColor = UIColor.white
}
The default border style is being impacted by the navigationItem. If you remove the border style and manually set the corner radius, it should display as white. White seems to be the only color I ran into being affected by this.
Change:
textFieldInsideSearchBar?.backgroundColor = UIColor.white
To:
textFieldInsideSearchBar?.borderStyle = .none
textFieldInsideSearchBar?.cornerRadius = 10
textFieldInsideSearchBar?.backgroundColor = .white
When I put the search bar in a table header, your code above (without altering the border/radius) worked without issue, but when I tried to embed it into a navigation bar item, I ran into your problem.

Default tableview cells don't respond to dark mode

Using a custom cell I'm able to get dark mode/normal mode to work properly. But when using the default framework cell Apple has provided it remains white regardless of what mode I enable. I read here
ios13 Dark Mode change not recognized by tableview Cell?
about the same problem. The answer tells me to use this:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
removeAndReaddGradientIfNeeded()
}
}
But I'm unsure how exactly I'm supposed to use this and how it relates to my cells. My code right now for my cells is this:
if #available(iOS 13, *) {
cell.backgroundColor = UIColor.systemBackground
cell.textLabel?.textColor = UIColor(named: "MainLabelColor")
cell.detailTextLabel?.textColor = UIColor(named: "SubLabelColor")
}
I use system color and custom colors in assets with two modes, one for light and one for dark. Now, this works fine in custom cell, but not in default.
Could anyone show me how to use the delegate function with cells?
Did you try to change the contentView background color? because the content view sits on top of the cell.
if #available(iOS 13, *) {
cell.contentView.backgroundColor = UIColor.systemBackground
//For named color you have to resolve it.
cell.textLabel?.textColor = UIColor(named: "MainLabelColor")?.resolvedColor(with: self.traitCollection)
cell.detailTextLabel?.textColor = UIColor(named: "SubLabelColor")?.resolvedColor(with: self.traitCollection)
//MARK:- Even If your Viewcontroller disabled dark mode, tableView cell will be enabled.
self.overrideUserInterfaceStyle = .unspecified
}
To Support Dark Mode make sure you removed following overrides:-
UserInterfaceStyle default value is unspecified . So, You might have enabled userInterfaceStyle to light in somewhere in your code or list file.
In Plist file check for following key-value and remove them:-
<key>UIUserInterfaceStyle</key>
<string>light</string>
In Code check for the following the line and remove them.
i) If the key window is overridden to light mode, your entire app will be forced to light mode.
UIApplication.shared.keyWindow?.overrideUserInterfaceStyle = .light
ii) If View Controller is overridden to light mode, your entire ViewController will be forced to light mode.
self.overrideUserInterfaceStyle = .light

Erase borders between navigation bar and searchBar swift 4

I am setting both the navigation bar and the search bar to a custom UIColor (which I call categoryColor in my code). When I do that, I still see an upper grayish line between nav bar and search bar. I have already set the searchBar border color to be the same as the others, but that gray line still exists. Does anyone know how to get rid of it?
Here is my code:
override func viewWillAppear(_ animated: Bool) {
//defining the color that will be used for all the items
let categoryColor = UIColor(hexString: selectCategory?.categoryColorHex ?? UIColor.randomFlat.hexValue())
//changing navigation bar tint color
navigationController?.navigationBar.barTintColor = categoryColor
//changing searchbar tint color
searchBar.barTintColor = categoryColor
//change searchBar border's color
searchBar.layer.borderColor = categoryColor?.cgColor
searchBar.layer.borderWidth = 3
//changing title that appears at the top, after list is loaded
title = selectCategory?.listName ?? "Todoey"
}
Here is a picture of what I see when I run the simulation:
The better way for implementing search bar with nav controller would be to use searchController inside a navigationController so the searchController will have the same background as navigationController. Here’s a great tutorial about that: https://m.youtube.com/watch?v=h7caxpp3Xps
Edit:
Also if you already implemented search capabilities you can do that with searchController too. Just set navigationController.searchConroller.searchBar.delegate for class that’s responsible for handling delegate methods

Why I cannot make my UITabBarController blurred?

I have a UITabBar and I want to make it blurred. I wrote the following code:
import UIKit
class TabBarController:UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let blur = UIBlurEffect(style: UIBlurEffectStyle.Light)
let blurView = UIVisualEffectView(effect: blur)
blurView.frame = self.view.bounds
blurView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
self.view.layer.insertSublayer(blurView, atIndex: 0)
}
}
but somehow the last line throws error:
Cannot convert value of type 'UIVisualEffectView' to expected argument
type 'CALayer'
how can I fix that?
I changed the last line to:
self.tabBar.addSubview(blurView)
but now the whole tabbar is blurred (even with icons and they are not visible). When I changed this line to:
self.tabBar.sendSubviewToBack(blurView)
then the tabbar is visible, but not blurred. I want to achieve effect from accepted answer from here Black background on transparent UITabBar but here it is uitabbar and I'm using uitabbarcontroller... Can you help me with applying blur in my case?
You just add the blur view as a subview:
self.view.addSubview(blurView)
Since you just want to blue the tab bar and this class is a tab bar controller, you can do:
self.tabBar.addSubview(blueView)
You also need to change the frame:
blurView.frame = self.tabBar.bounds
why don't you just use the barTintColor property on your TabBarController?
self.tabBar.translucent = true
self.tabBar.barTintColor = UIColor.blackColor()
You don't even need to subclass UITabBarController. You can call this on any UIViewController.
self.tabBarController?.tabBar.translucent = true
self.tabBarController?.tabBar.barTintColor = UIColor.blackColor()
If I understood correctly from the following comment that you posted, you want to change the UITabBar to be black in colour but still blurred.
And yes, I noticed that the UITabBarController is blurred by default, but I would like to make it blurred with specific style (.Dark).
Doing this since iOS 7 has actually become quite easy. Simply change the barStyle of your UITabBar to .black. Put the following code in your UIViewController's viewDidLoad method (note that UITabBar is translucent by default, so you don't need to specify that again).
tabBarController?.tabBar.barStyle = .black
If you want to set it back to the regular, white barStyle, change it back to .default.
tabBarController?.tabBar.barStyle = .default
You may even do this from within Interface Builder by selecting the Tab Bar in your UITabBarController's hierarchy and changing its Style to Black.
I have a solution, all you need is configure your UITabBar as following:
// next code will make tabBar fully transparent
tabBar.isTranslucent = true
tabBar.backgroundImage = UIImage()
tabBar.shadowImage = UIImage() // add this if you want remove tabBar separator
tabBar.barTintColor = .clear
tabBar.backgroundColor = .black // here is your tabBar color
tabBar.layer.backgroundColor = UIColor.clear.cgColor
If you want to add blur, do this:
let blurEffect = UIBlurEffect(style: .dark) // here you can change blur style
let blurView = UIVisualEffectView(effect: blurEffect)
blurView.frame = tabBar.bounds
blurView.autoresizingMask = .flexibleWidth
tabBar.insertSubview(blurView, at: 0)
As a result:
Attach bottom constraint to the bottom of the view instead of Safe Area
It just might not be a problem with your TabBar but with tableView constraints.
Tab bar is blurred by default.

Trying to change mail compose navigation bar text colour

I am trying to change the colour of the navigation bar text for the email compose view which I previously had working, however since I have changed the size and font of the navigation bar text throughout the app, this has broken my code.
I have this code in the view controller that segues to the view controller that contains the email button:
var attributes = [NSForegroundColorAttributeName: UIColor.whiteColor(),NSFontAttributeName: UIFont(name: "Avenir", size: 24)]
self.navigationController?.navigationBar.titleTextAttributes = attributes
This is the code I was previously using to change the navigation bar colour to white:
mc.navigationBar.tintColor = UIColor.whiteColor()
I have this code in viewDidLoad in one of my view controllers, but it doesn't affect the mail compose view controller:
UINavigationBar.appearance().tintColor = UIColor.whiteColor()
Any ideas?
mailCnt.navigationBar.tintColor = UIColor.whiteColor
should work