Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

topLayoutGuide in child view controller

I have a UIPageViewController with translucent status bar and navigation bar. Its topLayoutGuide is 64 pixels, as expected.

However, the child view controllers of the UIPageViewController report a topLayoutGuide of 0 pixels, even if they're shown under the status bar and navigation bar.

Is this the expected behavior? If so, what's the best way to position a view of a child view controller under the real topLayoutGuide?

(short of using parentViewController.topLayoutGuide, which I'd consider a hack)

like image 780
hpique Avatar asked Oct 02 '13 15:10

hpique


People also ask

What is child view controller in Swift?

Child view controllers are especially useful for UI functionality that we wish to reuse across a project. For example, we might want to display a loading view as we're loading the content for each screen — and that can easily be implemented using a child view controller, that can then simply be added when needed.

What is top layout guide?

The topLayoutGuide property comes into play when a view controller is frontmost onscreen. It indicates the highest vertical extent for content that you don't want to appear behind a translucent or transparent UIKit bar (such as a status or navigation bar).

How do I embed a view controller?

Just drag a container view out into your main view controller and use the embed segue from it to your embedded view controller. It will properly set up all the view controller hierarchy for you.

How do I add a view controller container view?

Add the view controller's view to your view hierarchy with addSubview (and also set the frame or constraints as appropriate). Call the didMove(toParent:) method on the child view controller, passing the reference to the parent view controller.


2 Answers

While this answer might be correct, I still found myself having to travel the containment tree up to find the right parent view controller and get what you describe as the "real topLayoutGuide". This way I can manually implement automaticallyAdjustsScrollViewInsets.

This is how I'm doing it:

In my table view controller (a subclass of UIViewController actually), I have this:

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];

    _tableView.frame = self.view.bounds;

    const UIEdgeInsets insets = (self.automaticallyAdjustsScrollViewInsets) ? UIEdgeInsetsMake(self.ms_navigationBarTopLayoutGuide.length,
                                                                                               0.0,
                                                                                               self.ms_navigationBarBottomLayoutGuide.length,
                                                                                               0.0) : UIEdgeInsetsZero;
    _tableView.contentInset = _tableView.scrollIndicatorInsets = insets;
}

Notice the category methods in UIViewController, this is how I implemented them:

@implementation UIViewController (MSLayoutSupport)

- (id<UILayoutSupport>)ms_navigationBarTopLayoutGuide {
    if (self.parentViewController &&
        ![self.parentViewController isKindOfClass:UINavigationController.class]) {
        return self.parentViewController.ms_navigationBarTopLayoutGuide;
    } else {
        return self.topLayoutGuide;
    }
}

- (id<UILayoutSupport>)ms_navigationBarBottomLayoutGuide {
    if (self.parentViewController &&
        ![self.parentViewController isKindOfClass:UINavigationController.class]) {
        return self.parentViewController.ms_navigationBarBottomLayoutGuide;
    } else {
        return self.bottomLayoutGuide;
    }
}

@end

Hope this helps :)

like image 169
NachoSoto Avatar answered Oct 13 '22 15:10

NachoSoto


I might be wrong, but in my opinion the behaviour is correct. The topLayout value can be used by the container view controller to layout its view's subviews.

The reference says:

To use a top layout guide without using constraints, obtain the guide’s position relative to the top bound of the containing view.

In the parent, relative to the containing view, the value will be 64.

In the child, relative to the containing view (the parent), the value will be 0.

In the container View Controller you could use the property this way:

- (void) viewWillLayoutSubviews {

    CGRect viewBounds = self.view.bounds;
    CGFloat topBarOffset = self.topLayoutGuide.length;

    for (UIView *view in [self.view subviews]){
        view.frame = CGRectMake(viewBounds.origin.x, viewBounds.origin.y+topBarOffset, viewBounds.size.width, viewBounds.size.height-topBarOffset);
    }
}

The Child view controller does not need to know that there are a Navigation and a Status bar : its parent will have already laid out its subviews taking that into account.

If I create a new page based project, embed it in a navigation controller, and add this code to the parent view controllers it seems to be working fine:

enter image description here

like image 11
vinaut Avatar answered Oct 13 '22 16:10

vinaut