I am programicaly loading Storyboards into a UITabBarController but for some reason UITabBarItem are not loading the image or selectedImage for all except the first Tab are not loading
But if you tap other tabs icons load
Loading Storyboard Example
import UIKit
class ProfileViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let defaultImag = UIImage(named: "Profile")?.withRenderingMode(.alwaysOriginal)
let selectedImag = UIImage(named: "Profile_selected")?.withRenderingMode(.alwaysOriginal)
self.tabBarItem = UITabBarItem(title: nil, image: defaultImag, selectedImage: selectedImag)
self.tabBarItem.imageInsets = UIEdgeInsets(top: 10, left: 0, bottom: -10, right: 0);
}
}
UITabBarController
import UIKit
class TabBarViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let discoverStoryboard = UIStoryboard(name: "Discover", bundle: nil)
let discoverViewController = discoverStoryboard.instantiateInitialViewController()! as UIViewController
let favouritesStoryboard = UIStoryboard(name: "Favourites", bundle: nil)
let favouritesViewController = favouritesStoryboard.instantiateInitialViewController()! as! FavouritesViewController
let profileStoryboard = UIStoryboard(name: "Profile", bundle: nil)
let profileViewController = profileStoryboard.instantiateInitialViewController()! as UIViewController
self.viewControllers = [discoverViewController, favouritesViewController, profileViewController]
}
}
private func createTabForStoryBoard(storyBoardName: String, title: String, imageName :String, tag: Int) -> (UINavigationController) {
let storyboard = UIStoryboard(name: storyBoardName as String, bundle: nil)
let viewController = storyboard.instantiateInitialViewController() as! UIViewController
let image = UIImage(named: imageName)
let tabBarItem:UITabBarItem = UITabBarItem.init(title: NSLocalizedString(title, comment: "Tab Bar Names") , image: image, tag: 0)
viewController.tabBarItem = tabBarItem
return viewController
}
// MARK: Custom Methods
func setTabBarViewControllers() {
let discoverViewController = self.createTabForStoryBoard("Discover", title: "Discover", imageName: "<image-name>", tag: 1)
let favouritesViewController = self.createTabForStoryBoard("Favourites", title: "Favourites", imageName: "<image-name>", tag: 2)
let profileViewController = self.createTabForStoryBoard("Profile", title: "Profile", imageName: "<image-name>", tag: 3)
let tabBarViewControllers = [discoverViewController, favouritesViewController, profileViewController]
self.viewControllers = tabBarViewControllers
}
Related
i have a UITabBarViewController that i have created programmatically and i had imported all my 3 UIViewControllers , Now I wanna add Coordinator to my Project and i want to add Coordinator to all my 3 UIViewControllers.
how can i Use It?
class MainTabbarViewController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// home Tab
let homeStoryboard: UIStoryboard = UIStoryboard(name: "HomeStoryboard", bundle: nil)
let homeTab = homeStoryboard.instantiateViewController(withIdentifier: "HomeViewController")
let homeTabBarItem = UITabBarItem(title: "Home", image: UIImage(systemName: "house"), selectedImage: UIImage(systemName: "house.fill"))
homeTab.tabBarItem = homeTabBarItem
// search Tab
let searchStoryboard: UIStoryboard = UIStoryboard(name: "SearchStoryboard", bundle: nil)
let searchTab = searchStoryboard.instantiateViewController(withIdentifier: "SearchViewController")
let searchTabBarItem = UITabBarItem(title: "Search", image: UIImage(systemName: "magnifyingglass"), selectedImage: UIImage(systemName: "magnifyingglass"))
searchTab.tabBarItem = searchTabBarItem
// profile Tab
let profileStoryboard: UIStoryboard = UIStoryboard(name: "ProfileStoryboard", bundle: nil)
let profileTab = profileStoryboard.instantiateViewController(withIdentifier: "ProfileViewController")
let profileTabBarItem = UITabBarItem(title: "Profile", image: UIImage(systemName: "person"), selectedImage: UIImage(systemName: "person.fill"))
profileTab.tabBarItem = profileTabBarItem
profileTab.navigationController?.navigationBar.isHidden = true
self.tabBar.tintColor = UIColor(asset: Asset.Colors.yellow)
self.viewControllers = [homeTab, searchTab, profileTab]
}
}
this is my UITabBarController
protocol Coordinator {
var childCoordinators: [Coordinator] { get set }
var navigationController: UINavigationController { get set }
func start()
}
and this is my Coordinator protocol.
this file should remove and add an other file.
i named TabCoordinator with this Code:
final class TabCoordinator: NSObject, TabBarCoordinatorProtocol {
// Root View Controller
var rootViewController: UIViewController {
return tabController
}
// Empty UITabBarController
let tabController: UITabBarController
// Tab Item Coordinators
let homeCoordinator: HomeCoordinator
let searchCoordinator: SearchCoordinator
let profileCoordinator: ProfileCoordinator
// Initialize
override init() {
tabController = UITabBarController()
var controllers: [UIViewController] = []
// Home Coordinator
homeCoordinator = HomeCoordinator(navigationController: UINavigationController())
let homeViewController = homeCoordinator.rootViewController
homeViewController.tabBarItem = UITabBarItem(title: "Home", image: UIImage(systemName: "house"), selectedImage: UIImage(systemName: "house.fill"))
homeCoordinator.start()
// Search Coordinator
searchCoordinator = SearchCoordinator(navigationController: UINavigationController())
let searchViewController = searchCoordinator.rootViewController
searchViewController.tabBarItem = UITabBarItem(title: "Search", image: UIImage(systemName: "magnifyingglass"), selectedImage: UIImage(systemName: "magnifyingglass"))
searchCoordinator.start()
// Profile Coordinator
profileCoordinator = ProfileCoordinator(navigationController: UINavigationController())
let profileViewController = profileCoordinator.rootViewController
profileViewController.tabBarItem = UITabBarItem(title: "Profile", image: UIImage(systemName: "person"), selectedImage: UIImage(systemName: "person.fill"))
profileCoordinator.start()
super.init()
controllers.append(homeViewController)
controllers.append(searchViewController)
controllers.append(profileViewController)
tabController.viewControllers = controllers
tabController.tabBar.tintColor = UIColor(asset: Asset.Colors.yellow)
tabController.tabBar.isTranslucent = false
}
}
and TabBarCoordinatorProtocol file incloude this codes
public protocol TabBarCoordinatorProtocol {
var rootViewController: UIViewController { get }
}
I have three viewcontrollers that all have buttons switch to a fourth viewcontroller. On this fourth viewcontroller, I have a back button, which I want to take me back to the viewcontroller that I was originally at.
I can't just use the control drag since that only let's you go to a single controller. How can I programmatically have it send the user to the most recent viewcontroller?
import UIKit
class ViewController: UIViewController {
let tabBar = UITabBarController()
var selectedIndex: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tab()
}
#IBAction func goto(_ sender: Any) {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "FrekansViewController") as! FrekansViewController
self.present(nextViewController, animated:true, completion:nil)
}
func tab() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let homeVC = UIViewController()
var streamVC = UIViewController()
var liveVC = UIViewController()
var searchVC = UIViewController()
streamVC = storyboard.instantiateViewController(withIdentifier: "StreamViewController")
liveVC = storyboard.instantiateViewController(withIdentifier: "LiveViewController")
searchVC = storyboard.instantiateViewController(withIdentifier: "SearchViewController")
tabBar.viewControllers = [homeVC,streamVC, liveVC, searchVC]
let itemHome = UITabBarItem(title: "Home", image: UIImage.init(systemName: "house.fill") , tag:0)
let itemStream = UITabBarItem(title: "List", image: UIImage.init(systemName: "waveform.path") , tag:1)
let itemLive = UITabBarItem(title: "Radio", image: UIImage.init(systemName: "play.fill") , tag:2)
let itemSearch = UITabBarItem(title: "Search", image: UIImage.init(systemName: "magnifyingglass"), tag: 3)
homeVC.tabBarItem = itemHome
streamVC.tabBarItem = itemStream
liveVC.tabBarItem = itemLive
searchVC.tabBarItem = itemSearch
self.view.addSubview(tabBar.view)
}
}
Try to embed the controllers inside NavigationController then you can use :
navigationController?.popViewController(animated: true)
This would do the desired functionality for you.
I have the following TabBarController with 2 items. It is showing correctly.
I'm calling the setupItems() function from another controller when something changes its value.
The function is called correctly, the problem is that the navFirstController.tabBarItem.image is not being updated.
class TabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
setupItems()
}
func setupItems() {
let scale: CGFloat = 0.35
let navFirstController = UINavigationController(rootViewController: FirstController())
let navSecondController = UINavigationController(rootViewController: SecondController())
navSecondController.tabBarItem.image = UIImage.scale(image: UIImage(named: "image2")!, by: scale)
navSecondController.tabBarItem.imageInsets = UIEdgeInsets(top: 8, left: 0, bottom: -8, right: 0)
if something == true {
navFirstController.tabBarItem.image = UIImage.scale(image: UIImage(named: "image1")!, by: scale)
} else {
navFirstController.tabBarItem.image = UIImage.scale(image: UIImage(named: "image3")!, by: scale)
}
navFirstController.tabBarItem.imageInsets = UIEdgeInsets(top: 8, left: 0, bottom: -8, right: 0)
viewControllers = [navSecondController, navFirstController]
}
}
I'ved tried with:
1) viewControllers?.remove(at: 1) at the beginning of setupItems()
2) navFirstController.removeFromParent() at the beginning of setupItems()
3) self.viewWillLayoutSubviews() at the end of setupItems()
4) self.view.setNeedsLayout(), self.view.setNeedsDisplay() at the end of setupItems()
I don't feel we need to create viewControllers object again just to change tab bar image.
Just we need to get viewController object from viewControllers array and change image.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnClicked(_ sender: Any) {
//change image of tab bar item on button clicked
if let tabVC = self.tabBarController as? TabBarController {
tabVC.changeImage()
}
}
}
class TabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
setupItems()
}
func setupItems() {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let firstVC = storyboard.instantiateViewController(withIdentifier: "First")
let navFirstController = UINavigationController(rootViewController: firstVC)
navFirstController.tabBarItem.image = UIImage(named: "Image1")
let secondVC = storyboard.instantiateViewController(withIdentifier: "Second")
let navSecondController = UINavigationController(rootViewController: secondVC)
navSecondController.tabBarItem.image = UIImage(named: "Image2")
viewControllers = [navSecondController, navFirstController]
}
func changeImage() {
if let second = viewControllers?[1] as? UINavigationController {
second.tabBarItem.selectedImage = UIImage(named: "Image3")
second.tabBarItem.image = UIImage(named: "Image3")
}
}
}
Note if you want to change selected tab bar item image then change "selectedImage" value otherwise change "image" value.
You probably need to set the image's rendering mode to UIImageRenderingModeAlwaysOriginal.
Try changing this:
navFirstController.tabBarItem.image = UIImage.scale(image: UIImage(named: "image1")!, by: scale)
With this:
navFirstController.tabBarItem.image = UIImage.scale(image: UIImage(named: "image1")!, by: scale).withRenderingMode(.alwaysOriginal)
EDIT - Sample Code
Consider this setup:
The initial view controller is a custom class TabBarViewController
The red background view controller is a UIViewController with storyboard ID "First"
The orange background view controller is a custom class SecondViewController with an IBAction and storyboard ID "Second"
The Assets.xcassets file has three images (40x40 png):
TabBarViewController
import UIKit
class TabBarViewController: UITabBarController {
var something: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
setupItems()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
func setupItems() {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let firstVC = storyboard.instantiateViewController(withIdentifier: "First")
let navFirstController = UINavigationController(rootViewController: firstVC)
navFirstController.tabBarItem.image = UIImage(named: "image1")!.withRenderingMode(.alwaysOriginal)
let secondVC = storyboard.instantiateViewController(withIdentifier: "Second")
let navSecondController = UINavigationController(rootViewController: secondVC)
navSecondController.tabBarItem.image = UIImage(named: "image2")!.withRenderingMode(.alwaysOriginal)
navSecondController.tabBarItem.imageInsets = UIEdgeInsets(top: 8, left: 0, bottom: -8, right: 0)
if something == true {
navFirstController.tabBarItem.image = UIImage(named: "image3")!.withRenderingMode(.alwaysOriginal)
} else {
navFirstController.tabBarItem.image = UIImage(named: "image1")!.withRenderingMode(.alwaysOriginal)
}
navFirstController.tabBarItem.imageInsets = UIEdgeInsets(top: 8, left: 0, bottom: -8, right: 0)
viewControllers = [navSecondController, navFirstController]
}
}
SecondViewController
import Foundation
import UIKit
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnClicked(_ sender: Any) {
//change image of tab bar item on button clicked
if let tabVC = self.tabBarController as? TabBarViewController {
tabVC.something = !tabVC.something
tabVC.setupItems()
}
}
}
OUTPUT
I have an app with a tableView. I decided to add more screens with other tableViews, so I added a tabBarController programatically. Now I get a found nil error on these lines:
tableView.delegate = self
tableView.dataSource = self
If I remove them, the tableView doesn't load. Do you know what I might be doing wrong?
I added a tabBarController on main storyboard an linked it to the swift file, but it also doesn't work.
class SecondVC: UIViewController,UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var SoundsClasss = [SoundsClass]()
override func viewDidLoad() {
super.viewDidLoad()
let p1 = SoundsClass(imageURL: "sound 01", audioURL: "01", videoTitle: "1", duration: 100)
let p2 = SoundsClass(imageURL: "sound 01", audioURL: "02", videoTitle: "2", duration: 100)
SoundsClasss.append(p1)
SoundsClasss.append(p2)
tableView.delegate = self
tableView.dataSource = self
}
The code for the TabBarController. Do I have to change anything here to specify that the view is a tableView?
class MainTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
tabBar.barTintColor = UIColor.white
setupTabBar()
}
func setupTabBar(){
let FirstController = UINavigationController(rootViewController: MainVC())
let SecondController = UINavigationController(rootViewController: SecondVC())
let ThirdController = UINavigationController(rootViewController: ThirdVC())
viewControllers = [FirstController, SecondController,ThirdController]
guard let items = tabBar.items else { return }
for item in items {
item.imageInsets = UIEdgeInsets(top: 4, left: 0, bottom: -4, right: 0)
}
}
}
in class named SecondVC you have an outlet reference for your UITableView instance this means you create your table view in Stroyboard, so you can not create the view controller using the initializer you should use
UIStoryboard(name: "yourStoryBoardName", bundle: nil).instantiateViewController(withIdentifier: "yourViewcontrollerID")
the setupTabBar function should be as the following
func setupTabBar(){
let vc1 = UIStoryboard(name: "yourStoryBoardName", bundle: nil).instantiateViewController(withIdentifier: "yourFirstViewcontrollerID")
let vc2 = UIStoryboard(name: "yourStoryBoardName", bundle: nil).instantiateViewController(withIdentifier: "yourSecondViewcontrollerID")
let vc3 = UIStoryboard(name: "yourStoryBoardName", bundle: nil).instantiateViewController(withIdentifier: "yourThirdViewcontrollerID")
let FirstController = UINavigationController(rootViewController: vc1)
let SecondController = UINavigationController(rootViewController: vc2)
let ThirdController = UINavigationController(rootViewController: vc3)
viewControllers = [FirstController, SecondController,ThirdController]
guard let items = tabBar.items else { return }
for item in items {
item.imageInsets = UIEdgeInsets(top: 4, left: 0, bottom: -4, right: 0)
}
}
You're using SecondVC() to create your controller. That does not reference the storyboard so it does not set up any outlets.
You need to either build your view controller hierarchy in the storyboard and let it load by default or else get your controllers from the storyboard before adding them to the navigation controllers.
See documentation for:
func instantiateViewController(withIdentifier identifier: String) -> UIViewController
I use tabBarController to create a music program and I have questions like how to do it as shown in gif
Questions:
How to do so that when you click on tabBarItem, "presentViewController" worked
How to make it so that the photo does not change color and make it round, only in the third tabBarItem
Preferably without libraries
it should be
My TabBarController
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
// меняет цвет фона tabBar
self.tabBar.barTintColor = .white
// меняет цвет UITabBarItem and Title
UITabBar.appearance().tintColor = UIColor(hex: 0x0077fe, alpha: 1)
//меняет цвет background UITabBar
UITabBar.appearance().barTintColor = UIColor.white
// делает фон серым
for item in self.tabBar.items! {
if let image = item.image {
item.image = image.withRenderingMode(.alwaysOriginal)
}
}
//показывает и переходит в контроллеры
let storyBoard = UIStoryboard(name: "Main", bundle:nil)
let controller1 = storyBoard.instantiateViewController(withIdentifier: "main") as! VCMain
let controller2 = storyBoard.instantiateViewController(withIdentifier: "search")
let controller3 = storyBoard.instantiateViewController(withIdentifier: "player")
let controller4 = storyBoard.instantiateViewController(withIdentifier: "bookmark")
let controller5 = storyBoard.instantiateViewController(withIdentifier: "menu")
self.setViewControllers([controller1,controller2,controller3,controller4,controller5], animated: true)
// создает навигационный контроллер для контроллеров
let vc1 = UINavigationController(rootViewController: controller1)
let vc2 = UINavigationController(rootViewController: controller2)
let vc3 = UINavigationController(rootViewController: controller3)
let vc4 = UINavigationController(rootViewController: controller4)
let vc5 = UINavigationController(rootViewController: controller5)
viewControllers = [vc1, vc2, vc3, vc4, vc5]
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
print(item.tag)
if item.tag == 0{
if GlobalModals.count != 0 {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "player") as? VCPlayer
self.present(vc!, animated: true, completion: nil)
}
}
}
Player
override func viewDidLoad() {
super.viewDidLoad()
let im = Extension.resizeImage(image:GlobalModals[thisSong].ImageView! , targetSize: CGSize.init(width:20, height: 20))
self.tabBarController?.tabBar.items![2].image = im
}
}
The TabBarController doesn't have those option, you need to implement it by subclassing.
You can use this library Animated Tab Bar to achieve the same result with animation.
I created a view and a button on it in tabBarController and created a func that sets the parameters and call it after I set all the view controllers also created a notificationCenter that could change the button image from another controller
class TabBarController: UITabBarController,UITabBarControllerDelegate {
open var playerBtn = UIButton()
//func for NotificationCenter
#objc func imageChange() {
if GlobalModals.count != 0 {
playerBtn.setImage(GlobalModals[thisSong].ImageView, for: .normal)
}
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(imageChange), name: NSNotification.Name(rawValue: "imageChange"), object: nil)
self.delegate = self
//shows and goes to controllers
let storyBoard = UIStoryboard(name: "Main", bundle:nil)
let controller1 = storyBoard.instantiateViewController(withIdentifier: "main") as! VCMain
let controller2 = storyBoard.instantiateViewController(withIdentifier: "search")
let controller3 = storyBoard.instantiateViewController(withIdentifier: "player")
let controller4 = storyBoard.instantiateViewController(withIdentifier: "bookmark")
let controller5 = storyBoard.instantiateViewController(withIdentifier: "menu")
self.setViewControllers([controller1,controller2,controller4,controller5], animated: true)
// creates navigation controller
let vc1 = UINavigationController(rootViewController: controller1)
let vc2 = UINavigationController(rootViewController: controller2)
let vc3 = UINavigationController(rootViewController: controller3)
let vc4 = UINavigationController(rootViewController: controller4)
let vc5 = UINavigationController(rootViewController: controller5)
viewControllers = [vc1, vc2, vc3, vc4, vc5]
self.setupMiddleButton()
}
// TabBarButton – Setup Middle Button and View
func setupMiddleButton() {
playerBtn.frame = CGRect(x: 0, y: 0, width: 45, height: 45)
var playerBtnFrame = playerBtn.frame
playerBtnFrame.origin.y = self.view.bounds.height - playerBtnFrame.height - 2
playerBtnFrame.origin.x = self.view.bounds.width / 2 - playerBtnFrame.size.width / 2
playerBtn.frame = playerBtnFrame
playerBtn.layer.cornerRadius = playerBtnFrame.height/2
playerBtn.layer.masksToBounds = true
playerBtn.contentMode = .scaleAspectFill
let view = UIView()
let width = self.view.frame.width/5
let xWidth = width*2
view.frame = CGRect(x: xWidth , y: playerBtnFrame.origin.y - 2, width: self.view.frame.width/5, height: tabBar.frame.height)
view.backgroundColor = .clear
self.view.addSubview(view)
self.view.addSubview(playerBtn)
playerBtn.setImage(UIImage(named: "home"), for: UIControlState.normal)
playerBtn.addTarget(self, action: #selector(playerButtonAction), for: UIControlEvents.touchUpInside)
self.view.layoutIfNeeded()
}
// Button Touch Action
#objc func playerButtonAction(sender: UIButton) {
if GlobalModals.count != 0 {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "player") as? VCPlayer
self.present(vc!, animated: true, completion: nil)
}
}
}
so I change the image of the button
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageChange"), object: nil)