Calculate iOS7 view insets

Guides | Tutorial By 3 years ago

Update: This is now available on GitHub, along with Unit Tests and updated for the bottom layout guide

In iOS7 a view controller now positions it’s view from the top of itself compared to previously starting below the navigation bar. If you use a single scroll view you can set automaticallyAdjustsScrollViewInsets to YES to automatically handle things for you, however if your UI is more complicated then you may need to do things manually. The view controller has a topLayoutGuide property which is useful in some cases, but unfortunately doesn’t take parent view controllers into consideration, or the view’s origin inside the view controller.

Take the following example of a view controller inside a navigation controller with two UIScrollViews. The left scroll view sits all the way up to the top of the screen, however the right scroll view sits half way between the navigation bar. The left one can simply use the topLayoutGuide to be given a of 64. The scroll view on the right needs to have some trickier logic, the topLayoutGuide minus the origin.y of the scroll view (22).


If instead our scroll view is in a child view controller (UINavigationController -> UIViewController -> child UIViewController) then we would need to know to query the parent view controller’s topLayoutGuide, and also take our own view’s position into consideration, as well as the scroll view and navigation bar.

I built a method a while back that does just this, it works on parent view controllers, child view controllers, custom frame view controllers, popover view controllers, and any other configuration you can think of.

@implementation UIViewController (Insets)

- (UIEdgeInsets) insetsForView:(UIView*) view
	UIViewController* viewController = self;
	//	Until root or a navigation controller
	if(![viewController isKindOfClass:[UINavigationController class]])
		while(viewController.parentViewController != nil && ![viewController.parentViewController isKindOfClass:[UINavigationController class]])
			viewController = viewController.parentViewController;
	const CGPoint convertedOrigin = [viewController.view convertPoint:view.bounds.origin fromView:view];
	const CGFloat topLayoutGuideLength = (([viewController respondsToSelector:@selector(topLayoutGuide)]) ? [viewController.topLayoutGuide length] : 0);
	const UIEdgeInsets rawInsets = UIEdgeInsetsMake(topLayoutGuideLength - convertedOrigin.y, 0, 0, 0);
	return UIEdgeInsetsMake(MAX(0, + ABS(MIN(0, self.view.frame.origin.y + convertedOrigin.y)),
							MAX(0, rawInsets.left),
							MAX(0, rawInsets.bottom),
							MAX(0, rawInsets.right));


So now I can write:

@implementation CustomViewController

- (void) viewWillLayoutSubviews
	[super viewWillLayoutSubviews];
	const UIEdgeInsets insets = [self insetsForView:self.scrollView];
	[self.scrollView setContentInset:insets];