I was recently asked to remove an older framework from one of our applications and replace it with a more up to date (iPhone X compatible) replacement. As I started to create this replacement our company shifted our plan to something different, but I didn’t want to lose the last weeks worth of work/updates to the project to get everything working smoothly. That is why I will show you the cool stuff that I worked on this week! (Finished Repo Link at the bottom!)

OK so first create a single view Application (for my demo I named it new Tab Bar). In your main view controller you want to add a collectionview and a Container View. I set my container view to Align Trailing, Leading, and Top to the Safe Area, and the Bootom Space to the Collection View. My Collection View constraints are set to: Height of 60, Align Trailing, Leading, and Bottom to the Safe Area, and Top Space to the Container View. Go ahead and delete the defualt view controller that you are given when setting up a Container View Controller. In your collectionView Cell add an imageView and an label. Set the constraints for the image view to: Trailing space and leading space of 10 to your cell (mines a class called tabCell), your top space equal to the cell and your bottom space to the label. Set your label contraints to: trailing leading bottom to your cell, height of 15, and top to your imageView. Then select your collectionView and set the Scroll Direction to Horizontal. And your cell size to 60X60. Set the reuse identifier to “tabCell”

Then setup your Outlets to your ViewController (Mine is named MainViewController) and in your viewDidLoad set your collectionView delegate and dataSource. Create the extensions to your MainViewController adhearing to the protocols. For now in DataSource just return 1 for numberOfItemsInSection and return UICollectionViewCell() in cellForItemAtIndex. Create a new Class called TabCollectionViewCell and hookup assign the class for your tab cell. Setup your Outlets for the imageView and the Label. It should look something like this when you are done (note my rendering shot of the label isn’t showing so I highlighted it.):

Now we are all set to begin Programming!!

I personally like to tackle the smallest problem first, so I would setup the TabCollectionViewCell. Override your awakeFromNib and prepareForReuse calling a function named updateWithImage. Your updateWithImage should take in an optional image and set that imge to your tabImageView.image property. If nothing is passed in set it to nil.

Since we are working with custom Tabs I created a Tab Model (named Tab becasue I am so creative!) I set my class Tab to have a tabName:String and a tabImageName:String and set the init to save these new values.

Time to get to our MainViewController. Setup a private Tab var and make it a list of Tab, a private selectedIndex to hold our index selection. and in view didLoad set that selectedIndex = 0. Also in viewDidLoad before you set your collectionViewDelegate and Datasource, call a new function addingTabs to your tabs list. in your addTabs function setup your home Tab, and some tabs after that (note for my project I themed it power rangers megazords and added some of the classics as well as one of the newer megazords.

Update your numberOfItemsInSection to your tabs.count, and update your cellForItemAt indexPath to return your tabCell (make sure to use collectionView.dequeueReusableCell(withReuseIdentifier: “tabCell”, for: indexPath) as! TabCell). Since it is much nicer to have the selected index colored differently we should set that up here. I set my selected Index tab label to blue, none selected tabs labels are black. In your Delegate extension create didSelectItemAt indexPath and set your selectedIndex to the indexPath.row.

Go back to your selectedIndex declaration and add a didSet to it calling a new function setViewController(). in setViewController (private func) we are going to handle updating our container view to the selected view controller. First create a var viewController = UIViewController() this will be our placeholder for our viewController and all of the values we will update. Then switch on your selectedIndex with a defualt of just presenting a UIViewController. I put all of my view controllers in a navigation controller so I can set titles and navigationItem buttons. Now you may be asking how we are going to keep up with our view controllers and for that I have an awesome answer that I have referenced a few times. Create a private var activeViewController: UIViewController and add a didSet to removeInactiveViewController(inactiveViewController:oldValue) and updateActiveViewController(). Since I didn’t come up with these functions I am just going to put them here and let you read how they work, because they are freaking awesome:

private func removeInactiveViewController(inactiveViewController: UIViewController?) { if let inActiveVC = inactiveViewController { inActiveVC.willMove(toParentViewController: nil) inActiveVC.view.removeFromSuperview() inActiveVC.removeFromParentViewController() } } private func updateActiveViewController() { if let activeVC = activeViewController { addChildViewController(activeVC) activeVC.view.frame = containerView.bounds containerView.addSubview(activeVC.view) activeVC.didMove(toParentViewController: self) } }

Now back in our setViewController set your activeViewController to your navigationController with the viewController as root.

We are now going to take a break from our MainViewController and work on some basic viewControllers and our home View Controller. Since I like easy stuff first we will start with our basic view controllers. Create some ViewControllers either by storyboard or xibs and set the background color to different colors in viewDidLoad. I added a imageview and a label and put them in different spots on my view controllers just in case someone has trouble with colors. These views/labels will be our tab information set up in our MainViewController and also why we needed the placeholder viewController property. Now that those are done, lets get to work on our HomeViewController.

I created a new storyboard named Home and set the initial view to a standard View Controller. I added in a tableView and a custom cell with the reuseIdentifier “HomeTabCell”. I created a class called HomeTabCell: UITableViewCell and added an imageView and a label to the cell. In HomeViewController.swift I created the outlet for our tableview, setup a new private var [Tab]() and created a different addTabs function because maybe I want my cells to be different than the collection view or more cells than the collection view for special reasons. In viewDidLoad set your tableView datasource and delegate and create the extensions. In your cell for Row create your tabCells and in your numberOfRowsInSection return your tabs.count.

Now for the fun part. When selecting a tableView cell how do you select the collection view cell and update the view controller? Well Delegation would be the answer! at the top of your MainViewController class create a protocol DidSelectTabFromTableDelegate: class . set a func in it that takes in an IndexPath! (mines named func scroll(to index: IndexPath!)). In your home view controller create a weak var of your delegate, and in your tableviewDelegate on didSelectRowAt indexpath have your delegate.scroll(to: indexPath).

Back in Your mainViewController, in setViewController set your default case to be your homeViewController and set the tabSelectDelegate = self. Create the extension adhearing to the protocol and in your function make sure to offset your index by 1 to account for your home collection view tab that shouldn’t be in your hometableview cells. if you have more than the tabs.count also handle that accordingly. Since we want some visual representation for the selection, I have collectionView.scrollToItem(at: indexOffset, at: .right, animated: true). And make sure to reloadData when you are done.

Finished Result: