Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS 7: Custom container view controller and content inset

I have a table view controller wrapped in a navigation controller. The navigation controller seems to automatically apply the correct content inset to the table view controller when it is presented via presentViewController:animated:completion:. (Can anyone explain to me how this works exactly?)

However, as soon as I wrap the combination in a custom container view controller and present that instead, the topmost part of the table view content is hidden behind the navigation bar. Is there anything I can do in order to preserve the automatic content inset behaviour in this configuration? Do I have to "pass through" something in the container view controller for this to work correctly?

I'd like to avoid having to adjust the content inset manually or via Auto Layout as I want to continue supporting iOS 5.

like image 667
tajmahal Avatar asked Sep 28 '13 08:09

tajmahal


3 Answers

I've had a similar problem. So on iOS 7, there is a new property on UIViewController called automaticallyAdjustsScrollViewInsets. By default, this is set to YES. When your view hierarchy is in any way more complicated than a scroll/table view inside a navigation or tab bar controller, this property didn't seem to work for me.

What I did was this: I created a base view controller class that all my other view controllers inherit from. That view controller then takes care of explicitly setting the insets:

Header:

#import <UIKit/UIKit.h>

@interface SPWKBaseCollectionViewController : UICollectionViewController

@end

Implementation:

#import "SPWKBaseCollectionViewController.h"

@implementation SPWKBaseCollectionViewController


- (void)viewDidLoad
{
    [super viewDidLoad];

    [self updateContentInsetsForInterfaceOrientation:self.interfaceOrientation];
}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [self updateContentInsetsForInterfaceOrientation:toInterfaceOrientation];

    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}

- (void)updateContentInsetsForInterfaceOrientation:(UIInterfaceOrientation)orientation
{
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
        UIEdgeInsets insets;

        if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
            insets = UIEdgeInsetsMake(64, 0, 56, 0);
        } else if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
            if (UIInterfaceOrientationIsPortrait(orientation)) {
                insets = UIEdgeInsetsMake(64, 0, 49, 0);
            } else {
                insets = UIEdgeInsetsMake(52, 0, 49, 0);
            }
        }

        self.collectionView.contentInset = insets;
        self.collectionView.scrollIndicatorInsets = insets;
    }
}

@end

This also works for web views:

self.webView.scrollView.contentInset = insets;
self.webView.scrollView.scrollIndicatorInsets = insets;

If there is a more elegant and yet reliable way to do this, please let me know! The hardcoded inset values smell pretty bad, but I don't really see another way when you want to keep iOS 5 compatibility.

like image 107
Johannes Fahrenkrug Avatar answered Oct 19 '22 23:10

Johannes Fahrenkrug


FYI in case anyone is having a similar problem: it appears that automaticallyAdjustsScrollViewInsets is only applied if your scrollview (or tableview/collectionview/webview) is the first view in their view controller's hierarchy.

I often add a UIImageView first in my hierarchy in order to have a background image. If you do this, you have to manually set the edge insets of the 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;

}
like image 22
BFar Avatar answered Oct 19 '22 23:10

BFar


Thanks to Johannes Fahrenkrug's hint, I figured out the following: automaticallyAdjustsScrollViewInsets indeed only seems to work as advertised when the child view controller's root view is a UIScrollView. For me, this means the following arrangement works:

Content view controller inside Navigation controller inside Custom container view controller.

While this doesn't:

Content view controller inside Custom container view controller inside Navigation controller.

The second option, however, seems more sensible from a logical point of view. The first option probably only works because the custom container view controller is used to affix a view to the bottom of the content. If I wanted to put the view between the navigation bar and the content, it wouldn't work that way.

like image 37
tajmahal Avatar answered Oct 20 '22 00:10

tajmahal