The concept

We are going to replace the default UITabBar of a UITabBarController, with a side menu. By doing so, we will be able to work on the children view controllers directly on the storyboard.

Setting up the project

Open XCode

  1. Create a new “Tabbed application”.
  2. Open the storyboard and drop a UINavigationController in. Make it initial view controller.
  3. Delete the default root view controller of the previously added UINavigationController.
  4. Make the UITabBarController the new root view controller of the navigation controller.
  5. Hide the tab bar. (Select it from the document outline and then check the View - Hidden checkbox)
  6. Drag and drop a UIBarButtonItem on the tab bar controller. Set the title to “Menu”.
  7. Create a new swift class (File→New→File…→Cocoa touch class), subclass of UITabBarController. Name it MyTabViewControler
  8. Back on the storyboard, set the class of your TabBarController to the newly created class.

Make it work

Go to your MyTabViewControler class and add the following code:

class MyTabViewControler: UITabBarController {
    
    var expanded = false
    
    override func viewDidLoad() {
        addGradientLayer()
        let menuView = UITableView(frame: CGRect(x: 0, y: 0, width: 200, height: CGRectGetHeight(view.bounds)))
        menuView.dataSource = self
        menuView.delegate = self
        navigationController?.view.superview?.insertSubview(menuView, atIndex: 0)
    }
    
    func addGradientLayer() {
        let gLayer = CAGradientLayer()
        gLayer.frame = CGRect(x: -8, y: 0, width: 8, height: CGRectGetHeight(view.bounds))
        gLayer.colors = [UIColor.lightGrayColor().CGColor, UIColor.clearColor().CGColor]
        gLayer.startPoint = CGPoint(x: 1, y: 0.5)
        gLayer.endPoint = CGPoint(x: 0, y: 0.5)
        navigationController?.view.layer.addSublayer(gLayer)
    }
    
    func toggleMenuExpansion() {
        UIView.animateWithDuration(0.2, animations: { () -> Void in
            self.navigationController!.view.frame.origin.x = self.expanded ? 0 : 200
            }) { (finished) -> Void in
                self.expanded = !self.expanded
        }
    }
    
    @IBAction func MenuTapped(sender: UIBarButtonItem) {
        toggleMenuExpansion()
    }
    
}

On viewDidLoad() we are adding a tableview behind the navigation controller and are setting the delegate and datasource to self. The gradient layer is the shadow which will emphasize the fact that the side menu is below the nagivation controller.

The toggleMenuExpansion() animates the navigation controller’s x origin to 0 or 200 if expanded is true / false respectively.

What left to do now, is to set the delegate/datasource methods for side menu:

extension MyTabViewControler: UITableViewDataSource {
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .Default, reuseIdentifier: nil)
        cell.textLabel?.text = viewControllers?[indexPath.row].tabBarItem.title
        cell.imageView?.image = viewControllers?[indexPath.row].tabBarItem.image
        return cell
    }
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return (self.viewControllers?.count)!
    }
}

extension MyTabViewControler: UITableViewDelegate {
    
    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 40))
    }
    
    func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 40
    }
    
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        //add a small delay (0.2 seconds)
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 200_000_000), dispatch_get_main_queue()) { () -> Void in
            self.selectedIndex = indexPath.row
            self.toggleMenuExpansion()
        }
        
    }
}

Run the app, it should work like this:

Bottom line

That was just a simple way to make a UITabBarController behave like a side menu. You can download the sample project from from here.