Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITabBarController - Incorrect and Inconsistent Bounds of Child (Tab) ViewControllers

I have a UITabBarController with two tabs. Each tab is a UITableViewController.

When the UITabBarController appears, the two tab views each have incorrect bounds. The first tab sits correctly under the navigation bar, but extends below the tab bar at the bottom. The second tab is the other way around, starting underneath the navigation bar but stopping correctly before the tab bar at the bottom.

enter image description here

I'm creating and presenting the TabBarController as follows:

ActiveListTabBarViewController* listTabBarController = [[ActiveListTabBarViewController alloc] initWithListController:_listController];
UINavigationController* nc = [[UINavigationController alloc] initWithRootViewController:listTabBarController];
[self presentViewController:nc animated:YES completion:^(){}];

And then in the init for the TabBarController, creating and adding the child (tab) views as follows:

_todoListViewController = [[BasicTableViewController alloc] initWithList:[_controller itemsToDo]];
_todoListViewController.delegate = self;
_todoListViewController.title = @"To Do";
_completedListViewController = [[BasicTableViewController alloc] initWithList:[_controller itemsDone]];
_completedListViewController.delegate = self;
_completedListViewController.title = @"Completed";

[self setViewControllers:@[_todoListViewController, _completedListViewController]];

What am I doing wrong?

Thanks, Gavin

Update: following the suggestion to add the following method to BasicTableViewController:

- (UIRectEdge)edgesForExtendedLayout
{
    return UIRectEdgeNone;
}

The behaviour of the first tab has improved and is positioned correctly, but the second tab remains the same. Situation now as follows:

enter image description here

Any suggestions? Cheers.

like image 321
Gavin Hope Avatar asked Jan 09 '14 21:01

Gavin Hope


2 Answers

The problem was caused by the way in which I was presenting the UITabBarController

ActiveListTabBarViewController* listTabBarController = [[ActiveListTabBarViewController alloc] initWithListController:_listController];
UINavigationController* nc = [[UINavigationController alloc] initWithRootViewController:listTabBarController];
[self presentViewController:nc animated:YES completion:^(){}];

Returning to Apple's docs, I wasn't sure if this was a valid way to present a UITabBarController. That is, presenting it as the child of another view controller.

It's not. Following are some snippets that confirmed it for me; from this, and the resulting change, I'm assuming it's not correct to present a TabBarController as I have, above.

From: https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/TabBarControllers.html

Before creating a tab bar interface, you need to decide how you intend to use a tab bar interface. Because it imposes an overarching organization on your data,you should use one only in these specific ways:

  • Install it directly as a window’s root view controller.
  • Install it as one of the two view controllers in a split view interface. (iPad only)
  • Present it modally from another view controller.
  • Display it from a popover. (iPad only)

From: https://developer.apple.com/library/ios/documentation/uikit/reference/UITabBarController_Class/Reference/Reference.html

Unlike other view controllers, a tab bar interface should never be installed as a child of another view controller.

And further clarification:

A tab bar controller is a container view controller that you use to divide your app into two or more distinct modes of operation.

A navigation controller presents data that is organized hierarchically and is an instance of the UINavigationController class. The methods of this class provide support for managing a stack-based collection of content view controllers.

The reason I presented the UITabBarController as the root view of a navigation controller, is that I wanted the navigation bar...

This is how I'm achieving that now, inside the init for the TabBarController:

- (id)initWithListController:(BasicListController *)controller
{
    self = [super init];
    if (self) {
        _controller = controller;

        _todoListViewController = [[BasicTableViewController alloc] initWithList:[_controller itemsToDo]];
        _todoListViewController.delegate = self;
        _todoListViewController.title = @"To Do";

        _completedListViewController = [[BasicTableViewController alloc] initWithList:[_controller itemsDone]];
        _completedListViewController.delegate = self;
        _completedListViewController.title = @"Completed";

        UINavigationController* ncTodo = [[UINavigationController alloc] initWithRootViewController:_todoListViewController];
        UINavigationController* ncCompleted = [[UINavigationController alloc] initWithRootViewController:_completedListViewController];

        [self setViewControllers:@[ncTodo, ncCompleted]];

        UIBarButtonItem* doneButton = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStylePlain target:self action:@selector(doneTap:)];
        _todoListViewController.navigationItem.leftBarButtonItem = doneButton;
        _completedListViewController.navigationItem.leftBarButtonItem = doneButton;
    }
    return self;
}

Note, I didn't have to do anything with:

  • edgesForExtendedLayout
  • automaticallyAdjustsScrollViewInsets
  • extendedLayoutIncludesOpaqueBars

The iOS 7 defaults respect the navigation bar and the tab bar (unlike the original screen shots above, when the UITabBarController was presented incorrectly).

like image 191
Gavin Hope Avatar answered Oct 06 '22 00:10

Gavin Hope


try implementing this method in your view controller:

- (UIRectEdge) edgesForExtendedLayout
{
    return UIRectEdgeNone;
}
like image 30
ManicMonkOnMac Avatar answered Oct 06 '22 01:10

ManicMonkOnMac