Content falls under the navigation bar when embedding container views in a user controller.

UPDATE

Based on Tim's answer, I implemented the following in every view controller that had a scrollview (or subclass) that was part of my custom container:

- (void)didMoveToParentViewController:(UIViewController *)parent { if (parent) { CGFloat top = parent.topLayoutGuide.length; CGFloat bottom = parent.bottomLayoutGuide.length; // this is the most important part here, because the first view controller added // never had the layout issue, it was always the second. if we applied these // edge insets to the first view controller, then it would lay out incorrectly. // first detect if it laid out correctly with the following condition, and if // not, manually make the adjustments since it seems like UIKit is failing to do so if (self.collectionView.contentInset.top != top) { UIEdgeInsets newInsets = UIEdgeInsetsMake(top, 0, bottom, 0); self.collectionView.contentInset = newInsets; self.collectionView.scrollIndicatorInsets = newInsets; } } [super didMoveToParentViewController:parent]; } 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I have a custom container view controller named SegmentedPageViewController . I set this as the UINavigationController rootViewController .

The purpose of the SegmentedPageViewController is to allow the UISegmentedControl , specified as the header header of the NavController, to switch between different child view controllers.

enter image description here

All of these child view controllers contain either a scrollview, a tableview, or a collection.

We find that the first view controller loads normally, correctly located under the navigation bar. But when we switch to the new view manager, the navigation bar is not respected, and the view is set under the navigation bar.

enter image description here

We use automatic layout and interface builder. We have tried everything that we can think of, but cannot find an agreed solution.

Here is the main block of code responsible for setting up the first view controller and switching to another when the user clicks on the segmented control:

 - (void)switchFromViewController:(UIViewController *)oldVC toViewController:(UIViewController *)newVC { if (newVC == oldVC) return; // Check the newVC is non-nil otherwise expect a crash: NSInvalidArgumentException if (newVC) { // Set the new view controller frame (in this case to be the size of the available screen bounds) // Calulate any other frame animations here (eg for the oldVC) newVC.view.frame = self.view.bounds; // Check the oldVC is non-nil otherwise expect a crash: NSInvalidArgumentException if (oldVC) { // **** THIS RUNS WHEN A NEW VC IS SET **** // DIFFERENT FROM FIRST VC IN THAT WE TRANSITION INSTEAD OF JUST SETTING // Start both the view controller transitions [oldVC willMoveToParentViewController:nil]; [self addChildViewController:newVC]; // Swap the view controllers // No frame animations in this code but these would go in the animations block [self transitionFromViewController:oldVC toViewController:newVC duration:0.25 options:UIViewAnimationOptionLayoutSubviews animations:^{} completion:^(BOOL finished) { // Finish both the view controller transitions [oldVC removeFromParentViewController]; [newVC didMoveToParentViewController:self]; // Store a reference to the current controller self.currentViewController = newVC; }]; } else { // **** THIS RUNS WHEN THE FIRST VC IS SET **** // JUST STANDARD VIEW CONTROLLER CONTAINMENT // Otherwise we are adding a view controller for the first time // Start the view controller transition [self addChildViewController:newVC]; // Add the new view controller view to the view hierarchy [self.view addSubview:newVC.view]; // End the view controller transition [newVC didMoveToParentViewController:self]; // Store a reference to the current controller self.currentViewController = newVC; } } } 
+46
ios objective-c ios7 uinavigationbar
Sep 26
source share
4 answers

The custom container view controller will need to configure the contentInset second view controller according to your known height of the navigation bar, given the automaticallyAdjustsScrollViewInsets property of the child view controller. (You may also be interested in the topLayoutGuide property of your container - make sure that it returns the correct value during and after the view switch.)

UIKit is remarkably inconsistent (and buggy) in how it applies this logic; sometimes you will see that it automatically performs this setup for you, reaching several view controllers down in the hierarchy, but often after a custom container switch you will need to do this work yourself.

+23
26 Sep '13 at 23:05
source share

It all seems a lot easier than people see.

UINavigationController will set scrollview inserts only when subzones are selected. However, addChildViewController: does not call the layout, so after calling it, you just need to call setNeedsLayout on your navigation controller. Here's what I do when switching views in a custom tab view:

 [self addChildViewController:newcontroller]; [self.view insertSubview:newview atIndex:0]; [self.navigationController.view setNeedsLayout]; 

The last line will cause scrollview inserts to be recalculated for the new controller content.

+20
26 Oct. '15 at 11:21
source share

FYI, if anyone has a similar problem: this problem can occur even without built-in view controllers. It seems like automaticallyAdjustsScrollViewInsets only applies if your scrollview (or tableview / collectionview / webview) is the first view in its view manager hierarchy.

I often add a UIImageView first to my hierarchy to have a background image. If you do this, you must manually set the inserts to scrollview in viewDidLayoutSubviews:

 - (void) viewDidLayoutSubviews { CGFloat top = self.topLayoutGuide.length; CGFloat bottom = self.bottomLayoutGuide.length; UIEdgeInsets newInsets = UIEdgeInsetsMake(top, 0, bottom, 0); self.collectionView.contentInset = newInsets; } 
+9
Aug 17 '14 at 18:28
source share

I found a better solution using the undocumented UINavigationController method.

 #import <UIKit/UIKit.h> @interface UINavigationController (ContentInset) - (void) computeAndApplyScrollContentInsetDeltaForViewController:(UIViewController*) controller; @end 

 #import "UINavigationController+ContentInset.h" @interface UINavigationController() - (void)_computeAndApplyScrollContentInsetDeltaForViewController:(id)arg1; @end @implementation UINavigationController (ContentInset) - (void) computeAndApplyScrollContentInsetDeltaForViewController:(UIViewController*) controller { if ([UINavigationController instancesRespondToSelector:@selector(_computeAndApplyScrollContentInsetDeltaForViewController:)]) [self _computeAndApplyScrollContentInsetDeltaForViewController:controller]; } @end 

then do this:

 - (void) cycleFromViewController: (UIViewController*) oldC toViewController: (UIViewController*) newC { [oldC willMoveToParentViewController:nil]; [self addChildViewController:newC]; [self transitionFromViewController: oldC toViewController: newC duration: 0.25 options:0 animations:^{ newC.view.frame = oldC.view.frame; [self.navigationController computeAndApplyScrollContentInsetDeltaForViewController:newC]; } completion:^(BOOL finished) { [oldC removeFromParentViewController]; [newC didMoveToParentViewController:self]; }]; } 
0
Aug 30 '15 at 9:15
source share



All Articles