How to set bar button item background image globally in iOS 13 - ios

iOS 13 has a whole new set of classes for configuring navigation bars and bar button items:
UIBarAppearance
UINavigationBarAppearance
UIBarButtonItemAppearance
UIBarButtonItemStateAppearance
How do I use these to give bar button items a global appearance? Let's say I want all bar button items to have a common background image. I used to say:
UIBarButtonItem.appearance().setBackgroundImage(im, for:.normal, barMetrics:.default)
This seems to be just what is superseded by these new classes. So what's the right way now?

It's verbose, but in some ways it's clearer. You can configure a navigation bar appearance and assign it to the navigation bar's standardAppearance through the proxy:
let app = UIBarButtonItemAppearance()
app.normal.backgroundImage = im
let navbarapp = UINavigationBarAppearance()
navbarapp.configureWithOpaqueBackground()
navbarapp.buttonAppearance = app
UINavigationBar.appearance().standardAppearance = navbarapp
The only problem with that is that it assigns the same background image to back button items. So if that's not desired, you have to code defensively, assigning back button items an empty (not nil) image:
let app = UIBarButtonItemAppearance()
app.normal.backgroundImage = im
let navbarapp = UINavigationBarAppearance()
navbarapp.configureWithOpaqueBackground()
navbarapp.buttonAppearance = app
let back = UIBarButtonItemAppearance()
back.normal.backgroundImage = UIImage() // prevent back button item
navbarapp.backButtonAppearance = back
UINavigationBar.appearance().standardAppearance = navbarapp

Related

Why does UIToolBar turn transparent/translucent when table view scrolls under it

I have a UITableViewController and when the user taps an "edit" button, I display a toolbar at the bottom of the screen. I create a shadow on top of the toolbar. It looks great (until I changed the color to make it more obvious shat's going wrong). When I scroll the table up, as soon as the bottom of the table comes out from behind the toolbar, the toolbar colors all become transparent or do something that looks transparent.
Here is a picture of the screen when the table has data that flows under the toolbar:
Here is a picture of the screen as I drag the table up and the last item is only partly under the toolbar:
Finally, when I drag the table as fr up as I can and the last item is completely above the toolbar, I get this:
It is obvious that the toolbar background is changing opacity as the table is scrolling. I need to disable this. I have tried various combinations of checking and unchecking the "under bottom bars" and "under opaque bars" in the storyboard. I have tried adding an empty footer and not adding an empty footer to the table. Here is the code that creates the toolbar. Note that I'm using a table view controller within a tab bar controller and hiding the tab bar when the user edits the table:
public func setEditingWithTabController( _ editing: Bool, animated: Bool, tabController: UITabBarController ) {
if navigationController == nil || navigationController!.toolbar == nil {
return
}
navigationController!.setToolbarHidden( !editing, animated: true )
var items = [UIBarButtonItem]()
let flexible = UIBarButtonItem( barButtonSystemItem: .flexibleSpace, target: self, action: nil )
deleteButton = UIBarButtonItem(title: Strings.Delete.localized, style: .plain, target: self, action: #selector(didPressDelete))
deleteAllButton = UIBarButtonItem(title: "Delete All", style: .plain, target: self, action: #selector(didPressDeleteAll))
items.append( deleteAllButton! )
items.append( flexible )
items.append( deleteButton! )
tabController.setToolbarItems( items, animated: false ) // toolbar gets its items form the current view controller
let bar = navigationController!.toolbar!
bar.backgroundColor = AppSettings.appBackgroundColor.isDark ? AppSettings.appBackgroundColor.lighten( amount: 0.05 ) : AppSettings.appBackgroundColor.darken( amount: 0.05 )
bar.barTintColor = AppSettings.appBackgroundColor.isDark ? AppSettings.appBackgroundColor.lighten( amount: 0.05 ) : AppSettings.appBackgroundColor.darken( amount: 0.05 )
bar.layer.shadowColor = UIColor.red.cgColor //AppSettings.appBackgroundColor.isDark ? UIColor.lightGray.cgColor : UIColor.lightGray.cgColor
bar.layer.shadowOpacity = AppSettings.appBackgroundColor.isDark ? 0 : 0.5
bar.layer.shadowOffset = CGSize.zero
bar.layer.shadowRadius = AppSettings.appBackgroundColor.isDark ? 0 : 20
bar.layer.borderColor = UIColor.clear.cgColor
bar.layer.borderWidth = 0
bar.clipsToBounds = false
bar.isTranslucent = false
bar.isOpaque = true
tableView.setEditing( editing, animated: true )
if editing {
refreshControl?.removeFromSuperview()
tableView.backgroundView = nil
} else {
refreshControl = UIRefreshControl()
refreshControl!.addTarget( self, action: #selector( refreshData(_:) ), for: .valueChanged )
tableView.backgroundView = refreshControl
}
}
This same thing happened before I added any of the shadow layer code but was much less obvious with the background simply changing from white to light gray when I scrolled the table.
The function above gets called when the user taps the "edit" button in the app header. Obviously I don't need to configure the toolbar every time this happens and especially when hiding it - I'll worry about fixing all of that later. I need to figure out why I can't get a clean unchanging toolbar. heck, I can't even seem to get rid of the gray line at the top of the toolbar even though I'm setting the border size to zero. This thing has a mind of its own!
[CORRECTION]...
I was not setting a background color. Now when I set a background color, I get the translucency only under the toolbar in the safe area inset like this:
In iOS 15, UIKit has extended the usage of the scrollEdgeAppearance, which by default produces a transparent background, to all navigation bars. The background is controlled by when your scroll view scrolls content behind the navigation bar. Your screenshots indicate that you are scrolled to the top, and so the navigation bar has selected its scrollEdgeAppearance over the standardAppearance that it would use when scrolled, and on previous versions of iOS.
To restore the old look, you must adopt the new UINavigationBar appearance APIs, UINavigationBarAppearance. Remove your existing customizations and do something like this:
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = <your tint color>
navigationBar.standardAppearance = appearance;
navigationBar.scrollEdgeAppearance = navigationBar.standardAppearance
In the general case, it is the last line navigationBar.scrollEdgeAppearance = navigationBar.standardAppearance, which resolves the issue by having the UINavigationBar use the same appearance for both its standard and edge states. Also note that this will cause the scroll view to underlap the navigation bar – we recommend against setting UINavigationBar.isTranslucent = true.
You can also use the appearance proxy with the code above, but substituting navigationBar.appearance().scrollEdgeAppearance = appearance for the last line (as you are constructing your own appearance object – the idea is to just make sure both scrollEdge and standard appearances are the same).
Credit: https://developer.apple.com/forums/thread/682420
Same can be achieved for Tabbar
let appearance = UITabBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = <your tint color>
self.tabBarController?.tabBar.standardAppearance = appearance;
self.tabBarController?.tabBar.scrollEdgeAppearance = self.tabBarController?.tabBar.standardAppearance

NavigationBar color changes not carried through to other views with UINavigationBarAppearance()

I’m trying to work this out in Xcode 12.5.1 so I can move to Xcode 13. I have run this app in Xcode 13 Released and that’s how I discovered the problem. I know I’m a little late to the game but before Xcode 13… it wasn’t broken.
In my apps I have custom themes setup in three configurations, summer, fall and winter. Each theme sets the color attributes of the navBar as well as those of the rest of the controls in the app. The theme is chosen on the settings VC from a segmented control.
The setNavbarAttributes() code is run from AppDelegate in didFinishLaunchingWithOptions. It sets the correct navBar attributes when the app launches. In this case the code work as expected. I can verify this by navigating to the other views in the app and observing that the navBar shows the correct color attributes.
The setNavbarAttributes() code also runs when a theme is chosen from the segmented control on the settings VC. Here is the problem. When the theme is changed the navBar color attributes are not carried through to the navBar in the other views. Does anybody know why this isn’t working? I have a not so great workaround by putting the settings VC update code in an extension but that means touching every VC. That doesn't seem right.
It worked fine with my old code shown below but that’s broken in Xcode 13 with UINavigationBarAppearance().
let theNavebarProperties = UINavigationBar.appearance()
theNavebarProperties.barTintColor = Theme.current.navbarColor
theNavebarProperties.isTranslucent = false
theNavebarProperties.titleTextAttributes = [.foregroundColor: Theme.current.accentColor, .font: UIFont.systemFont(ofSize: gNavBarTitleTextSize, weight: .semibold)]
This sets the attributes of the navBar across the app.
class SetNarbar
{
static func setNavbarAttributes()
{
let theAppearance = UINavigationBarAppearance()
theAppearance.configureWithOpaqueBackground()
theAppearance.backgroundColor = Theme.current.navbarColor
theAppearance.titleTextAttributes = [.foregroundColor: Theme.current.accentColor, .font: UIFont.systemFont(ofSize: gNavBarTitleTextSize, weight: .semibold)]
UINavigationBar.appearance().standardAppearance = theAppearance
UINavigationBar.appearance().scrollEdgeAppearance = theAppearance
UINavigationBar.appearance().compactAppearance = theAppearance
}
}
This code is run in the settings VC on viewDidLoad and when the theme selector is tapped.
// Sets the controls on the settings VC to the currently selected theme. I have omitted code that works and does not pertain to setting the navBar.
func updateThemeOnSettingsVC()
{
setNeedsStatusBarAppearanceUpdate()
SetNarbar.setNavbarAttributes()
let navAppearance = UINavigationBarAppearance()
navAppearance.configureWithOpaqueBackground()
navAppearance.backgroundColor = Theme.current.navbarColor
navAppearance.titleTextAttributes = [.foregroundColor: Theme.current.accentColor, .font: UIFont.systemFont(ofSize: gNavBarTitleTextSize, weight: .semibold)]
navigationItem.standardAppearance = navAppearance
navigationItem.scrollEdgeAppearance = navAppearance
navigationItem.compactAppearance = navAppearance
}

How to select tab bar item without selecting it

I am using swift and I have a problem with the tab bar. I want to select a tab bar item, but without open it, what I mean: I want to change its color like it is selected but without the selection is it possible to be done. Here is a image: https://imgur.com/a/IdPituS , I want the top left item (burger menu item) color to be like the first one and the first one color to be light (like not selected).
Try to edit the image of the item you want to highlight (selecting without selecting).
You can try something like this:
let image = shouldHighlight ? UIImage(named: "highlightItem") : UIImage(named: "normalStyle")
myViewController.tabBarItem = UITabBarItem(title: "text", image: image, selectedImage: UIImage(named: "selectedImage"))
Then you update the viewControllers property in you tab bar viewController
myTabViewController.viewControllers
You can pre-select the tabBar item
like this:
tabBarController?.selectedIndex = 0 //select whatever You want
and
colorTint the unselected index
tabBar = UITabBarController()
tabBar?.tabBar.barTintColor = UIColor.white //bar Color
tabBar?.tabBar.tintColor = .red //Icons color
tabBar?.tabBar.unselectedItemTintColor = .white //Unselected item Colors

Set TabBar Item Disabled Color

My Current tabBar :
Expected tabbar with disabled tabs(ignore the language its for sample purpose):
I am able to disable items from my first tab bar image by using this :
self.isEnabled = false
For Not loggedin users i disabled these items but i want to change their color to something dull. How to do that.
Note : I am not asking for changing color in unselected state. I am asking only for the disabled state.
Add your following tab bar images as white.
Like
And in you MainStoryboard select your Main TabBarController, then change the image tint to Red (whatever color you need to show the image color)
Then you can see that the unselected TabBar Items color looks disabled.
To make look like disabled tabbar item.
if let arrayOfTabBarItems = tabBarViewController.tabBar.items as! AnyObject as? NSArray,tabBarItem = arrayOfTabBarItems[2] as? UITabBarItem {
tabBarItem.enabled = false
}

UIDocumentPickerViewController - How to set the navigation bar color

I want to set the navigation bar background color of my DocumentPicker to be the same as the rest of my application.
let filePicker = UIDocumentPickerViewController(documentTypes: ["public.content"], in: .import)
filePicker.delegate = self
filePicker.navigationController?.navigationBar.barTintColor = self.theme.navigationBarColor
self.present(filePicker, animated: true, completion: nil)
doesn't work.
Other things I've tried:
Use UINavigationBar.appearance().backgroundColor = self.theme.navigationBarColor - doesn't work and looks too much like a workaround instead of a proper way if it works.
Edit:
Right now our app is redesigned to use the primary color as navigation bar text color, and have the same background color as the DocumentPicker. Answers will still be appreciated.
This will change the color of the text like "Cancel".
UIBarButtonItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName:UIColor.blue], for: .normal)
and this will change the arrow back color.
UIButton.appearance().tintColor = UIColor.blue
Don't forget to set it back to your original colors after closing the picker if it required.

Resources