UINavigationController embedded in a container view, displays a Table View Controller with wrong size
ios, objective-c, uicontainerview, uitableview
Solution
This issue still exists in iOS 10. A UINavigationController embedded in a container view, which in turn is contained in a UITabBarController, will lay out all views in the navigation stack with extra space for a "phantom" tab bar at the bottom.
The easiest solution is to subclass UINavigationController and return 'nil' from `tabBarController`.
class MyNavigationController: UINavigationController {
override var tabBarController: UITabBarController? {
return nil
}
}
This makes the views in the navigation stack think they don't have a tab bar controller, so they won't leave extra space for it during layout. I haven't noticed any negative side effects from this fix, but obviously the views in this navigation controller's stack will no longer have access to the tab bar controller. If that's a problem, you can use a more generic method to find the tab bar controller (or any "parent" view controller).
For example, if the main view controller for your app is a UITabBarController named "MainViewController", you can extend UIViewController with a convenience method to find it.
extension UIViewController {
func mainViewController() -> MainViewController? {
var vc: UIViewController? = self
while !(vc is MainViewController) && vc != nil {
vc = vc?.parent ?? vc?.navigationController ?? vc?.presentingViewController
}
return vc as? MainViewController
}
}
This works because a tab bar controller is the `parent` of its direct child view controllers. The above method works its way up the chain of parent, presenting and nav controllers to eventually reach a child of the tab bar controller, which returns the tab bar controller as its parent.
Problem
I'm trying to find a workaround to this situation: I have a UITabBarController one of its segue is connected to a container view controller (BannerViewController) that I'm using to embed a UINavigationController, the navigation controller pushes other container view controllers (EventListContainerviewController) each of this contains a tableview controller. Here a screen from my story board The problem is that the last container view is displayed in a smaller frame than their containers. It seems that they loose the tabbar space at the bottom. I've forced all view controllers, navigation controller and tabbar controller to avoid do not extent their edges and adjust insets. The color means: Red: main view of the BannerContainerViewController 0x7fcc6d38bb00 light green : the container view of the BannerContainerViewController 0x7fcc6d38b860 Blue: main view of the EventListContainerViewController 0x7fcc6bd7b7c0 Orange: the container view of the EventListContainerViewController 0x7fcc6bd7b690 It seems that something changes when the navigation controller add the blue container view reducing its size by the same amount of a tabbar (49pt). It can be also seen in the recursive description of the view hierarchy: ``` <UIWindow: 0x7fcc6bd5af40; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x7fcc6bd4dd80>; layer = <UIWindowLayer: 0x7fcc6bd317c0>> | <UILayoutContainerView: 0x7fcc6bd671c0; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x7fcc6bd66de0>> | | <UITransitionView: 0x7fcc6bd6a980; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x7fcc6bd6ace0>> | | | <UIViewControllerWrapperView: 0x7fcc6d3a7b20; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x7fcc6d3a7db0>> | | | | <UIView: 0x7fcc6d38bb00; frame = (0 0; 320 519); autoresize = W+H; layer = <CALayer: 0x7fcc6d38bbd0>> | | | | | <UIView: 0x7fcc6d38b860; frame = (0 0; 320 519); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7fcc6d38b930>> | | | | | | <UILayoutContainerView: 0x7fcc6d398000; frame = (0 0; 320 519); autoresize = W+H; gestureRecognizers = <NSArray: 0x7fcc6d3a4b50>; layer = <CALayer: 0x7fcc6d387670>> | | | | | | | <UINavigationTransitionView: 0x7fcc6d39cfa0; frame = (0 0; 320 519); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x7fcc6d39cc40>> | | | | | | | | <UIViewControllerWrapperView: 0x7fcc6bf31230; frame = (0 0; 320 519); autoresize = W+H; layer = <CALayer: 0x7fcc6bf31300>> | | | | | | | | | <UIView: 0x7fcc6bd7b7c0; frame = (0 64; 320 406); autoresize = W+H; layer = <CALayer: 0x7fcc6bd7afe0>> | | | | | | | | | | <UIView: 0x7fcc6bd7b690; frame = (0 0; 320 406); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7fcc6bd7aac0>> | | | | | | | | | | | <UITableView: 0x7fcc6f03c400; frame = (0 0; 320 406); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x7fcc6bf34aa0>; layer = <CALayer: 0x7fcc6bf30bc0>; contentOffset: {0, 0}; contentSize: {320, 170}> | | | | | | | | | | | | <UITableViewWrapperView: 0x7fcc6bf35960; frame = (0 0; 320 406); gestureRecognizers = <NSArray: 0x7fcc6bf363e0>; layer = <CALayer: 0x7fcc6bf35ed0>; contentOffset: {0, 0}; contentSize: {320, 406}> | | | | | | | | | | | | | <EventCell: 0x7fcc6bca2950; baseClass = UITableViewCell; frame = (0 85; 320 85); autoresize = W; layer = <CALayer: 0x7fcc6bca2ca0>> ``` I've tries to use custom segue instead of container view try to forcing the layout, but I guess is a navigation controller fault. As you can see the banner is not overlapping the view as intended. Any suggestion?