change storyboard tab bar icon using swift - ios

I've created a tab bar controller and embedded several view controllers in it. I then try to set a specific tab bar item in a corresponding swift file for that view. Problem is that I am changing the item by overriding the "viewDidLoad" function. So it only updates after the user has touched that item. What be a better approach to changing storyboard tab bar items using swift?

create a subclass of UITabBarController
set this custom class of your TabBarController
now you override UITabBarController ViewDidLoad Method
there you can access all the TabItems and change their text/images
before a ViewController get loaded.
class CustomTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let allItems:[AnyObject] = self.tabBar.items!
var item1:UITabBarItem = allItems[0] as! UITabBarItem
var item2:UITabBarItem = allItems[1] as! UITabBarItem
item1.image = UIImage(named: "menu#2x.png")!.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
item2.image = UIImage(named: "play#2x.png")!.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
}
}
in custom class the code should be like as above...
you can set selectedState Image as well....
here is the result..

Related

UITabBar selected tab doesn't change tint color on launch

I have subclassed UITabBarController to allow for some customization specific to my app. It is the root view controller of my UIWindow and displays itself correctly on launch, and even shows the correct tab's view hierarchy as well.
The problem is with the selected tabbar item's tint color. Inside viewDidLoad of the custom tab bar controller subclass, I have set both the unselected and selected tint colors for the tab bar. See below:
override func viewDidLoad() {
super.viewDidLoad()
tabBar.tintColor = .tabBarItemActiveTint
tabBar.unselectedItemTintColor = .tabBarItemInactiveTint
tabBar.barTintColor = .tabBarBg
let dashboardVC = DashboardViewController.build()
let settingsVC = SettingsTableViewController.build()
let settingsNavC = UINavigationController(rootViewController: settingsVC)
settingsNavC.navigationBar.barStyle = .black
viewControllers = [dashboardVC, settingsNavC]
selectedViewController = dashboardVC
// Accessing the view property of each tab's root view controller forces
// the system to run "viewDidLoad" which will configure the tab icon and
// title in the tab bar.
let _ = dashboardVC.view
let _ = settingsVC.view
}
As you can see, the controller has its child view hierarchies set, and the views are loaded at the bottom so their respective viewDidLoad methods run where I have code that sets the tabBarItem. Here's an example from the dashboard view controller:
tabBarItem = UITabBarItem(title: "Dashboard", image: UIImage(named: Theme.dashboardTabBarIcon), tag: 0)
Everything about this works except for the selected icon and title. When the app launches I can see the tab bar, the first view hierarchy (the dashboard) is visible onscreen and the tabs all function properly. But the dashboard's icon and title are in an unselected state. I have to actually tap the tab bar icon to get the state to change such that it is selected.
Once you tap one of the tabs, the selected state works as normal. The issue is only on the first presentation of the tab bar.
Here is an image showing the initial state of the tab bar on launch. Notice the dashboard icon is not selected, even though it is the presented view controller.
UPDATE
Skaal's answer below solved the problem for me.
For future reference: the key difference between the code presented here in my question and his sample in the answer is that the tabBarItem is set in viewDidLoad of his custom TabBarController class. By contrast, that code was placed within the viewDidLoad method of each constituent view controller class in my project. There must be a timing issue of when things are called that causes the tint color to not be set in one scenario and work properly in the other.
Key Takeaway: If you set up a tab bar controller programmatically, be sure to set your tabBarItem properties early on to ensure tint colors work properly.
You can use :
selectedIndex = 0 // the index of your dashboardVC
instead of selectedViewController
EDIT - Here is a working sample of UITabBarController:
class TabBarController: UITabBarController {
private lazy var firstController: UIViewController = {
let controller = UIViewController()
controller.title = "First"
controller.view.backgroundColor = .lightGray
return controller
}()
private lazy var secondController: UIViewController = {
let controller = UIViewController()
controller.title = "Second"
controller.view.backgroundColor = .darkGray
return controller
}()
private var controllers: [UIViewController] {
return [firstController, secondController]
}
override func viewDidLoad() {
super.viewDidLoad()
tabBar.tintColor = .magenta
tabBar.unselectedItemTintColor = .white
tabBar.barTintColor = .black
firstController.tabBarItem = UITabBarItem(title: "First", image: UIImage(), tag: 0) // replace with your image
secondController.tabBarItem = UITabBarItem(title: "Second", image: UIImage(), tag: 1) // replace with your image
viewControllers = controllers
selectedViewController = firstController
}
}

navigation controller custom search bar not disappearing?

I created a custom search bar and embedding it in the navigation bar, it appears but after I push another view controller, the search bar does not get replaced with the title of the pushed view controller. The search bar stays persistent throughout all views, instead of getting replaced with a title. Perfect example is Instagram search tab, you search for a person and click on the cell, their profile is pushed and the search bar is replaced with the custom title, back button, etc.
First VC
self.customSearchBar.tag = 4
self.navigationController?.view.addSubview(customSearchBar)
Second VC
if let nav: UINavigationController = self.navigationController {
if let searchBar = nav.view.viewWithTag(4) {
searchBar.removeFromSuperview()
}
}
You shouldn't place the searchbar inside the navigationcontroller view as this view is the same instance on all pushed viewcontrollers.
Add the searchbar to the the depending view controllers ui.
To add a searchbar on navigationBar, this is the way.
self.navigationController?.navigationBar.addSubview(customSearchBar)
To remove it when you push it to other viewController. Write the following code in the secondVC that is pushed inside it's viewDidLoad() function. Also, set the tag of customSearchBar to any number (TAG)
if let nav: UINavigationController = self.navigationController {
let bar: UINavigationBar = nav.navigationBar
if let searchBar = bar.viewWithTag(TAG) {
searchBar.removeFromSuperview()
}
}
In the question, the customSearchBar is added to self.navigationController.view. To remove it, you can do the following:
if let nav: UINavigationController = self.navigationController {
if let searchBar = nav.view.viewWithTag(TAG) {
searchBar.removeFromSuperview()
}
}
Edit:
Adding and removing a UIViewController's view as a subview of other UIViewController
// for adding
let viewController: ViewController = ViewController()
self.addChildViewController(viewController)
self.view.addSubview(viewController.view)
viewController.view.bounds = self.view.bounds // better to use autolayout here
viewController.didMove(toParentViewController: self)
// for removing
if let vc = self.childViewControllers.last {
vc.willMove(toParentViewController: nil)
vc.view.removeFromSuperview()
vc.removeFromParentViewController()
}

What's the different with titles?

I would like to add title to my UIViewController:
I have tried
self.navigationController.navigationItem.title
self.navigationController.title
self.title
Sometimes, solution 1 work, sometimes solution 2 work, sometimes solution 3 works.
Can any expert tell me the different between them?
title is a property of UIViewController.
A localized string that represents the view this controller manages.
Set the title to a human-readable string that describes the view. If
the view controller has a valid navigation item or tab-bar item,
assigning a value to this property updates the title text in those
objects.
self.navigationController is a UINavigationController that manages the stack of viewControllers that your viewController is in. UINavigationController is a subclass of UIViewController, so self.navigationController.title is the title of the UINavigationController.
self.navigationItem.title:
The navigation item’s title displayed in the center of the navigation
bar. The default value is nil. When the receiver is on the navigation
item stack and is second from the top—in other words, its view
controller manages the views that the user would navigate back to—the
value in this property is used for the back button on the top-most
navigation bar. If the value of this property is nil, the system uses
the string “Back” as the text of the back button.
So, in practice, you should set the title of your ViewControllers. iOS will copy this title to the navigation item or tab bar item and display this title on the Navigation Bar if your ViewController is managed by a UINavigationController, it will use that text for the Back Button when you push to another ViewController, and it will display it in the tab bar if your ViewController is managed by a UITabBarController.
I create a demo to explain them:
You see, my vc1 is yellow-gray color embed in a navigation controller,my vc2 is light-green color embed in a navigation controller too, the two navigation controller is all manage by a tabbar controller .
In ViewController.swift(it is vc1), if I set self.title:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "vc1's title"
}
}
In ViewController2.swift(it is vc2):
import UIKit
class ViewController2: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "vc2's title"
}
}
The result is the tabbar title and navigation title all set:
If I set self.navigationController?.title:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// self.title = "vc1's title"
self.navigationController?.title = "vc1's nav title"
}
}
The result is tabbar title is set:
If I set self.navigationItem.title:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// self.title = "vc1's title"
//self.navigationController?.title = "vc1's nav title"
self.navigationItem.title = "vc1's navItem title"
}
}
The result is navigation title is set:

Swift 2 : Change navigation title on a view related to a Tab Bar Controller

I want to change the navigation's title of a view, which is related to a Tab Bar Controller, which is related to a Navigation Controller (see the picture)
I don't know how to do that.
With a basic view, I just need to do that in the ViewController.swift :self.title="test"
But here, this line changed the tab bar's title, but I want to change the navigation's title.
Main.Storyboard :
On Swift 3, in the UIViewController, override your viewDidAppear method and add this code snippet:
if let tabController = self.parent as? UITabBarController {
tabController.navigationItem.title = "My Title"
}
Use need to use this property:
self.navigationItem.title = "someTitle"
According to Apple best practices you should not have a tab bar controller contained inside of a navigation controller, rather you should have the view controller for each tab that requires one to be inside of it's own navigation controller.
There are various issues that can arise from having a tab bar controller contained within a navigation controller.
When implemented according to their standards you can set the title using self.title
An app that uses a tab bar controller can also use navigation controllers in one or more tabs. When combining these two types of view controller in the same user interface, the tab bar controller always acts as the wrapper for the navigation controllers.
https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/CombiningViewControllers.html
Embed UINavigationController in your storyboard and put:
navigationBar.topItem.title = "Nav title"
I also had difficulty to change a navigation bar title of a child view controller. The solution was:
#IBOutlet weak var navigationBar: UINavigationBar!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationBar.topItem!.title = "Pickup Address"
}
For Swift 3+
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationItem.title = "Title"
}

Can't set default button on tab bar in view controller

I have a View Controller with a UITabBar on the bottom. This tab bar performs a different function depending on which tab is selected. I would like to have one of the buttons selected when the view controller loads. I can't find a solution that doesn't involve a tabbarviewcontroller
class ViewController: UIViewController, UITabBarDelegate {
#IBOutlet var tabBar : UITabBar
//In view controller declare a UITabBar as an Outlet
override func viewDidLoad() {
super.viewDidLoad()
tabBar.selectedItem = tabBar.items[0] as UITabBarItem;// here you
can load the default button option which you want to load
}
func tabBar(tabBar: UITabBar!, didSelectItem item: UITabBarItem!) {
var tabBarValue = 0;
switch item.tag {
case 0:
tabBarValue = // assign value
break
case 1:
tabBarValue = // assign value
break
default:
break
}}
Pl. refer to the below url also, where they try to load different webview for the selected tab, Use a UITabBar without UITabBarController to control a UIWebView
This was the solution that worked out for me.
for i in tabBar.items as [UITabBarItem] {
if i.tag == 0 {
tabBar.selectedItem = i
}

Resources