iOS 5: Creating a custom side TabBar using Storyboards and Custom Segues

Recently I have seen people use tab bars that, when in landscape mode, run down the side of the iPad screen. I really like this as it takes up less screen space in general, but also that some of them are smaller in width than Apples own UITabBar, which at 59px I feel is larger than necessary. So in this tutorial I am going to show you how to create your own custom TabBar controller for managing multiple tabs. The entire source for this article can be downloaded from CustomTabBarExample. In response to this stack overflow question here is the DemoTabBar4.0. You can see the full video of this tutorial on YouTube.

Setting up the project.

First download the sample images from here. I have created both retina and non-retina make sure you only use the non-retina versions in interface builder. Opening up a new Xcode project and name the project CustomTabBarExample, unzip the example images and drag them into your project and with, Copying items into destination folder” selected hit finish.

Since we are creating a tab bar that will run down the side of the iPad in landscape mode set the supported device orientations to only support left and right landscape. This completes the setup of our blank project.

Building our CustomTabBarController

Select the Media Library, and drag out the background image for the CustomTabBar, placing it in the far left of the CustomTabBarController.

Notice that by adding image elements from the media library Xcode helpfully puts them inside a UIImageView for us. Add a UIView that covers the tab bar background and set its background colour to transparent. Add 3 buttons to this UIView. Each button has two images one dark grey and the other coloured (cyan or magenta). Set the darker of the two icons as the image for the button’s default state and the coloured image with the suffix _highlighted (this should really have been selected) for the selected state. Create an IBOutlet in the CustomTabBarViewController for the buttons UIView and hook it up so you can access them in code.

Note: These buttons could be added directly to the background UIImageView however, adding them to a UIView controller enables you to iterate through the buttons which as you will see later can be very useful.

Add a UIView that covers the remaining area of the CustomTabBarViewController, this will act as a placeholder for the content of the child views when switching tabs.

Create an IBOutlet in your CustomTabBarViewController class and to hook up the placeholder to it, be sure to synthesise this in the main.

Switching Tabs

Add a new UIViewController to your UIStoryboard and set its Size in the Attributes Inspector to be Freeform and uncheck resize from nib.

Select the UIView contained in the new UIViewController and in the size inspector set its dimensions to the same as those of the place holder (in this case they are 938,748). Copy this UIViewController two more times so we have one for each tab and set the background colour of each of the views housed in these UIViewControllers differently so you can see them change when you tap a tab.

In the CustomTabBarController implement the perform segue method like so.

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    if([segue.identifier isEqualToString:@"HomeSegue"] 
       || [segue.identifier isEqualToString:@"HeartSegue"]
       || [segue.identifier isEqualToString:@"CogSegue"]){
        
        for (int i=0; i<[self.buttons.subviews count];i++) {
            UIButton *button = (UIButton *)[self.buttons.subviews objectAtIndex:i];
            [button setSelected:NO];
        }
        
        UIButton *button = (UIButton *)sender;
        [button setSelected:YES];
    }
}

Here you can see why adding the buttons to a UIView is useful, rather than having to comparing individual buttons we just need to loop through the buttons on the view to deselect the current selected item. Then select the button tapped to reflect the tab that is being shown.

Creating the Custom segue

Connect the Home button up to its UIViewController by right clicking on the button and dragging to the UIViewController, in the resulting popup choose custom as the transition style.

We now have to define what this custom transition will look like. We do this by creating a subclass of UIStoryboardSegue and overriding the perform method like so,

 
- (void) perform {
 
    CustomTabBarViewController *src = (CustomTabBarViewController *)self.sourceViewController;
    UIViewController *dst = (UIViewController *) self.destinationViewController;
 
    for(UIView *view in src.placeholderView.subviews){
        [view removeFromSuperview];
    }
 
    src.currentViewController = dst;
    [src.placeholderView addSubview:dst.view];
}

This implementation will take the source of the segue, in our case the CustomTabViewController and remove all of the subviews of the placeholder view we setup before finally adding the subview of the destination controller to the placeholder view. You will also note here that I have added another property to the CustomTabViewController called src.currentViewController, this keeps a handle to the UIViewController that owns the view being displayed. Now you have to set each of the Segues to be of this type to ensure this transition is indeed what is being used and create identifiers for each Segue. If you are using my code from above make sure they are named correctly, HomeSegue, HeartSegue and CogSegue.

You should complete this process for your remaining two UIViewControllers and your finished UIStoryboard should look something like this.

As you can see it is very simple to create your own custom transitions with very little code. I hope this helps you to create your own navigation items.

49 Responses to “iOS 5: Creating a custom side TabBar using Storyboards and Custom Segues”

  1. Russ F

    As an iOS newbie, I was struggling with sticking with the recommended storyboards but going off-piste with my custom tab bar. Thanks for showing how simple custom segues are.

    Reply
    • Scott

      Glad I could be of help if there is anything that you have problems with or a tutorial you would like to see please feel free to let me know and I will do my best to put one together.

      Reply
  2. Christoph

    Hi, thx for this tut. The tab bar feels and looks great. But i think there is a problem. I made one of these views that get inserted in the placeholder a custom view controller and added a uitableview to it. Now when i touch the background inside this view, the app crashes because this view controller has already been deallocated.

    Reply
    • Christoph

      I think i maybe missed the part with storing a reference as the currentViewController, nice solution ;) but i still wonder if apple is gonna approve this thing because normally they say 1 screen 1 view controller

      Reply
      • Scott

        You won’t have any problem getting this past Apple, this is only one scene for one ViewController all you are doing is keeping a reference to prevent it being deallocated. This is exactly how you should manage your scenes the only slight thing that I am looking into is rather than holding onto a reference can the UIViewController be held in the window UIViewController hierarchy by adding it as a childViewController. However, I have never done this and I am still going through the documentation. If you look at UIViewController Containers I would be more than happy for us to try and extend this tutorial to get the optimal solution. Thanks.

        Reply
  3. Walter

    Dear Scott,

    I followed your custom tab bar tutorial! It’s great! I used it to navigate within my existing app. I stumbled upon a problem… when one of the UIviewcontrollers is added to the placeholderView in the CustomTabBarController the app crashes. It seams that as soon as the reloadData function is called on the tableView within that UIViewController the app seams to crash. I can’t seem to find the solution… perhaps you had this problem too? Do you know a way to overcome this problem?

    Thanks for the tutorial and thanks in advance for your help!

    Cheers from Holland!

    Reply
    • Scott

      I will take a look at this thanks for pointing it out. I will try to be as quick as possible with a response. Thanks Scott.

      Reply
      • vianney

        Hi,

        it seems that i’m stuck with the same problem, reloadData of a tableView in a UIViewController make the app crashes bad…
        If someone have find a solution to this, it could be very helpful to me!
        Thx from France!

        Reply
  4. Jon

    This tutorial is brilliant. However, I have one question. How would you make one of these ViewControllers the default. That is, upon opening the app, how would you load the home segue automatically?

    Reply
    • Scott

      All you have to do is call performSegue and pass it the name of the Segue you wish to be the default (HomeSegue). Now put this in your main scenes viewDidLoad and the home view should be the first one shown every time you load the app. I hope this answers the question, if not just let me know and I will try and get back to you. Thanks Scott.

      Reply
  5. vilavgfx

    Hi Scott,

    Thanks for the great tutorials. This one is particularly awesome.

    Is there a way to toggle the visibility of this CustomTabBar at the touch of a button? For instance, on tapping a button the TabBar would slide over and appear on top of the larger ViewController. That way the tabs are there when you need them only when you need them.

    Building on the concept of all your 6 tutorials so far, I have a request for a tutorial.

    Recently saw an iPad app called BMW Magazine.

    (See link: http://bit.ly/JY56FN)

    I was wondering if you can do a tutorial on how to build something similar. Specific aspects could include:

    1) Using Storyboard primarily to create separate ViewControllers

    2) Swiping backwards and forwards between the ViewControllers to navigate.

    3) Double Tapping to show a contents menu with thumbnails of each ViewController

    Thanks & Regards

    Reply
    • Scott

      Sure I have been looking for a new one to do and my schedule is clearing up a little so I will put this at the top of the list and try and get it to you sometime next week. Thanks Scott.

      Reply
    • Scott

      Sorry for the delay, however I found time yesterday and I have a working version just like the BMW Magazine app. The tutorial along with source will be available today I hope this is still useful for your. Thanks, Scott

      Reply
  6. vilavgfx

    Excellent Scott. Thanks a lot. Really looking forward to it.

    Regards
    Vilav

    Reply
  7. Robin

    Hi Scott,

    Thanks for this really nice tutorial. It’s working for me, however when I want to open open a new ViewController form one Tab ( from a button inside the Tab view, I open a ViewController with a modal segue), the app crashes every time.
    I’m really new to storyboard and I’m not sure about how it works. Any idea?
    Thanks!

    Reply
  8. Vilav

    Hey Scott,

    Any news on when you might have the next one up?

    Cheers
    Vilav

    Reply
    • Scott

      I am working on one at the moment based on the BMW app in the store. I have been really busy with work but my aim is to get it up this weekend. Thanks, Scott

      Reply
  9. Marco

    Hello Scott,

    thanks a lot for your tutorial, I’m applying it to my app and it looks really great. I have an issue though, and I’m not able to solve it. According to your tutorial, I created a custom tab bar with 5 buttons, 5 more view controllers, and for each of them I created a related Class. Within the Storyboard inspector of each ViewController, I changed the class names accordingly of course. Now, I want to use a right swipe within one of these controllers. But, when I play with this gesture, the app crashes. This does not occur within the main ViewController. And, BTW: if I do a standard modal segue for one of the other controllers, the tab bar’s gone, as expected, but the swipe’s been accepted. Any tips? What I’m missing?

    Thanks in advance for your help!

    Marco

    Reply
    • Scott

      Do you have any code you could share so I could have a look? The one thing I have subsequently found out about this tutorial is that I should be using UIViewController Containment when implementing custom controller components like this so I should have an update asap.

      Reply
  10. Marco

    Hello Scott,

    thanks very much for your reply. You can try yourself directly on your example, just create a ViewController class, assign it to any of the 3 view controllers on the StoryBoard, and add the following within the viewDidLoad method:
    UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(test)];
    rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
    [self.view addGestureRecognizer:rightSwipe];

    Create a basic “test” method such as:
    - (void)test
    {
    NSLog(@”this is a test”);
    }

    Compile and you’ll find that, when right swiping on that view controller, the app crashes. There’s an update though: after some attempts, I changed the type of property for placeholder and currentViewController from weak to strong, and looks like there are no more crashes. Do you see any downside in doing this?

    Thanks again,

    Marco

    Reply
  11. Heimizhou

    Hello Scott,
    I am an Android developer. Now I need a background picture of tab bar in iOS.
    But I can not find it in the Internet.
    Do you have it? And can you help me?
    Thank you!

    Reply
  12. greg

    Hello,

    How can I add next view?
    For example I want add button to second view and when I push the button go to another view.
    When I added button and made push relation from your second view to my new view app was crash.

    Reply
  13. Vikram

    Great tutorial you have here. Helped me gaining a lot of understanding about storyboards and custom segues. I have a small question about your implementation of custom tabbar. The destination controllers for all the segues is a subclass of UIViewController. What if I want to have a UIViewController wrapped in UINavigationController (for the purposes of having a UINavigationBar on top of my tabs)? When adding a subview, I understand that only the subview will be added not the navigationbar. What should be done in that case?

    Reply
  14. KRM

    Hi,
    what can I do to recover a NSNumber to the first TabBar(ViewControllerScan) from the second(ViewControllerData) with a storyboard segues “RelationShip View Controllers” ?
    Thanks for your help.

    Reply
  15. C

    Scott,

    First of all I love the tutorial, it is just what I needed for the app Im working on.

    In the Custom Tab Bar Controller viewDidLoad function I added a performSegueWithIdentifier… to essentially go to the home tab when the tab bar controller loads. My problem is that viewWillAppear and viewDidAppear dont get run in my home view controller when I do this(viewDidLoad does get run). Now if I go to another tab then click back on the home tab those two functions run properly.

    Any help is much appreciated.

    Reply
  16. Igor

    Thank you very much for the tutorial. The only one thing I can not understand now is how can I wrap UIViewController with NavigationController. Can anyone help?

    Thanks in advance.

    Reply
  17. C

    @igor just embed your tab bar controller in a UINavigationController and set it as the root view controller. If you have the Tab bar controller selected in the storyboard just go to Editor > Embed In > Navigation Controller

    Reply
  18. C

    I was having issues with willRotate/didRotate and willAppear/didAppear not being called on the individual child controllers after I segue’d them because the individual view controllers were just getting added as a subview to the custom tab bar controller and not getting properly added as a child controller similar to how a UINavigationController or UITabBarController works. A solution to this was to replace:

    [src.placeholderView addSubview:dst.view];

    with:

    [src addChildViewController:dst];
    [source.placeholderView addSubview:dst.view];
    [dst didMoveToParentViewController:src];

    in your custom segue class. This makes the app use new iOS 5 UIViewController containment.

    Reply
  19. infojunkie2012

    I am trying to use the last segue to display an interact with a UICollectionView. However, I cannot get the cells to display. I have created a class for the UICollectionView but none of the class routines are being called (like “numberOfItemsInSection”).

    I did the following:
    AlbumCoverCollectionView.h

    #import

    @interface AlbumCoverCollectionView : UICollectionView

    @end

    AlbumCoverCollectionView.m
    #import “AlbumCoverCollectionView.h”
    #import “Cell.h”

    NSString *kDetailedViewControllerID = @”DetailView”; // view controller storyboard id
    NSString *kCellID = @”cellID”; // UICollectionViewCell storyboard id

    @implementation AlbumCoverCollectionView

    - (id)initWithFrame:(CGRect)frame {

    self = [super initWithFrame:frame];

    if (self) {
    // Initialization code
    }

    return self;
    }

    - (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section {

    return 86;
    }

    - (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    // we’re going to use a custom UICollectionViewCell, which will hold an image and its label
    //
    Cell *cell = [cv dequeueReusableCellWithReuseIdentifier:kCellID forIndexPath:indexPath];

    // make the cell’s title the actual NSIndexPath value
    //cell.label.text = [NSString stringWithFormat:@"%ld", (long)indexPath.row + 1];

    // load the image for this cell
    NSString *imageToLoad = [NSString stringWithFormat:@"%d.jpg", indexPath.row + 1];
    cell.coverImage.image = [UIImage imageNamed:imageToLoad];

    //UIBezierPath *path = [UIBezierPath bezierPathWithRect:cell.image.bounds];
    //cell.image.layer.shadowPath = path.CGPath;

    return cell;
    }

    // the user tapped a collection item, load and set the image on the detail view controller
    //
    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

    }

    /*
    // Only override drawRect: if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    - (void)drawRect:(CGRect)rect
    {
    // Drawing code
    }
    */

    @end

    Cell.h
    #import

    @interface Cell : UICollectionViewCell

    @property (strong, nonatomic) IBOutlet UIImageView *coverImage;

    @end

    Cell.m
    #import “Cell.h”
    #import “CustomCellBackground.h”

    @implementation Cell

    - (id)initWithCoder:(NSCoder *)aDecoder {

    self = [super initWithCoder:aDecoder];

    if (self) {
    // change to our custom selected background view
    CustomCellBackground *backgroundView = [[CustomCellBackground alloc] initWithFrame:CGRectZero];
    self.selectedBackgroundView = backgroundView;
    }

    return self;
    }

    @end

    In the Storyboard, I removed the image already in the view, I added a UICollectionView, set it’s class AlbumCoverCollectionView, added a UICollectionCell and added a UIImageView the cell.

    At this point, I do not understand how to have the collection view display and set up the segue in order to interact with the view. Any ideas? I can send you the project if you care to have a look.

    Thanks in advance for your help.

    Reply
  20. ariel

    very nice tutorial, exactly what i need :) ,

    just one question: how can you access objects inside the uiviewcontroller displayed or called by the segue. for example: when the app loads it display it together with the home screen (called by performSegue)and the home view controller has a textfield and a button when the button click it display a message inside the textfield. but it always crash with exc_bad_access code=1.

    thank you in advance.

    Reply
    • Scott

      Hi Javier,
      I am not sure I understand what you mean can you clarify a little. Do you want to access the view inside a view controller in the prepareforSegue?

      Reply
      • ariel

        im sorry for the confusion:
        using performSegue i managed to load a default view controller (we can call it “home”). this Viewcontroller has uitextfield and button that when i click the button it will display a simple message in the textfield.
        with single view app i can just issue [txtMsg setText:@"hello world"]; but when i used this inside a view controller loaded by performSegue it always crash with exc_bad_access code=1
        i hope i explain it well, my apology for my grammar, but thank you very much for a very fast response.

        Reply
  21. Julio Zatarain

    Hi Scott,
    I found your tutorial useful.
    Only problem was that when profiling the project when I switched between tabs, memory usage kept growing.

    I solved it the following way,
    Keeping track of the viewcontroller of each tab assigning it to each button subclassing uibutton:
    Also everytime a segue is called i check if its assigned to the button or I assign it.

    I hope this helps someone, cheers!

    // CustomTabBarButton.h

    #import

    @interface CustomTabBarButton : UIButton
    @property (retain, nonatomic) UIViewController *ownedViewController;

    @end

    // CustomTabBarButton.m
    #import "CustomTabBarButton.h"

    @implementation CustomTabBarButton
    @synthesize ownedViewController=_ownedViewController;
    @end

    // CustomTabBarSegue.h

    #import

    @interface CustomTabBarSegue : UIStoryboardSegue
    @property (weak, nonatomic) UIViewController *vc;
    @end

    // CustomTabBarSegue.m
    #import "CustomTabBarSegue.h"
    #import "ViewController.h"

    @implementation CustomTabBarSegue

    @synthesize vc;

    - (void) perform{

    ViewController *src = [[ViewController alloc] init];
    src = self.sourceViewController;
    UIViewController *dst = (UINavigationController *) self.vc;

    for(UIView *view in src.myPlaceHolderView.subviews){
    [view removeFromSuperview];
    }
    src.currentViewController = self.vc;
    [src addChildViewController:dst];
    [src.myPlaceHolderView addSubview:dst.view];
    [dst didMoveToParentViewController:src];

    }
    @end

    And finally the prepare for segue method

    -(void) prepareForSegue:(CustomTabBarSegue *)segue sender:(id)sender
    {

    if([(CustomTabBarButton*) sender ownedViewController]==nil)
    [sender setOwnedViewController:segue.destinationViewController];

    segue.vc=[sender ownedViewController];

    }

    Reply
  22. ariel javier

    Scott,

    its me again :) . do you any tips or way how to preserve the data on the view controller? when i clicked or move to another view using preparesegue it removes all the data from the first view controller.

    thank you again.

    ariel

    Reply
    • Scott

      Hello Ariel,
      The MVC design pattern should aid you in thinking about how to do this correctly. Ideally you should consider you model as separate from your controller and persist that however you feel necessary. There are several ways you can do this, you can write the data to file, store it in core data or just have it load from the web each time.

      Hope this helps, if you give me a concrete example of what you are trying to do I will try and give you a more usable answer.

      Thanks,
      Scott

      Reply
  23. Anand

    Hi Scott,
    wonderful tutorial. However I am facing a problem.
    When I try to launch I am getting the following error:Application windows are expected to have a root view controller at the end of application launch

    What could be the reason for this?

    Regards,
    Anand

    Reply
    • Scott

      There is a small problem with this tutorial that I have commented on before. When adding subviews with separate view controllers to another view you must also add the view controller itself to the controller hierarchy. This was what you are seeing is this being enforced now. The all that is missing is [self addChildViewController:]; Hope this helps.

      Reply

Leave a Reply

  • (will not be published)

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>