Explaining difference between automaticallyAdjustsScrollViewInsets, extendedLayoutIncludesOpaqueBars, edgesForExtendedLayout in iOS7

83,278

Solution 1

Starting in iOS7, the view controllers use full-screen layout by default. At the same time, you have more control over how it lays out its views, and that's done with those properties:

edgesForExtendedLayout

Basically, with this property you set which sides of your view can be extended to cover the whole screen. Imagine that you push a UIViewController into a UINavigationController. When the view of that view controller is laid out, it will start where the navigation bar ends, but this property will set which sides of the view (top, left, bottom, right) can be extended to fill the whole screen.

Let see it with an example:

UIViewController *viewController = [[UIViewController alloc] init];
viewController.view.backgroundColor = [UIColor redColor];
UINavigationController *mainNavigationController = [[UINavigationController alloc] initWithRootViewController:viewController];

Here you are not setting the value of edgesForExtendedLayout, therefore the default value is taken (UIRectEdgeAll), so the view extends its layout to fill the whole screen.

This is the result:

without edges for extended layout, the view fills the whole screen

As you can see, the red background extends behind the navigation bar and the status bar.

Now, you are going to set that value to UIRectEdgeNone, so you are telling the view controller to not extend the view to cover the screen:

UIViewController *viewController = [[UIViewController alloc] init];
viewController.view.backgroundColor = [UIColor redColor];
viewController.edgesForExtendedLayout = UIRectEdgeNone;
UINavigationController *mainNavigationController = [[UINavigationController alloc] initWithRootViewController:viewController];

And the result:

with edgesForExtendedLayout set, the view is just under the navigation


automaticallyAdjustsScrollViewInsets

This property is used when your view is a UIScrollView or similar, like a UITableView. You want your table to start where the navigation bar ends, because you wont see the whole content if not, but at the same time you want your table to cover the whole screen when scrolling. In that case, setting edgesForExtendedLayout to None won't work because your table will start scrolling where the navigation bar ends and it wont go behind it.

Here is where this property comes in handy, if you let the view controller automatically adjust the insets (setting this property to YES, also the default value) it will add insets to the top of the table, so the table will start where the navigation bar ends, but the scroll will cover the whole screen.

This is when is set to NO:

the table will scroll over the whole screen, but starts out partially covered

And YES (by default):

the table scrolls over the whole screen, and starts just under the navigation

In both cases, the table scrolls behind the navigation bar, but in the second case (YES), it will start from below the navigation bar.


extendedLayoutIncludesOpaqueBars

This value is just an addition to the previous ones. By default, this parameter is set to NO. If the status bar is opaque, the views won't be extended to include the status bar, even if you extend your view to cover it (edgesForExtendedLayout to UIRectEdgeAll).

If you set the value to YES, this will allow the view to go underneath the status bar again.

If something is not clear, write a comment and I'll answer it.

How does iOS know what UIScrollView to use?

iOS grabs the first subview in your ViewController's view, the one at index 0, and if it's a subclass of UIScrollView then applies the explained properties to it.

Of course, this means that UITableViewController works by default (since the UITableView is the first view).

Solution 2

Not sure if you are using storyboards, but if you are, to make your view controllers start below the status bar (and above the bottom bar):

Select the view controller in IB, In the attributes inspector, deselect 'Extend Edges - Under Top Bars' and 'Extend Edges - Under Bottom Bars'.

Solution 3

I am using storyboards and using the above advice worked however I wasn't exactly sure how to implement it. Below is a short example in swift of how it cleared up the problem by putting the recommended solution into the ViewController.

import Foundation
import UIKit

// This ViewController is connected to a view on a storyboard that 
// has a scrolling sub view.
class TheViewController: UIViewController {

  // Prepares the view prior to loading.  Putting it in viewDidAppear didn't work.
  override func viewWillAppear(animated: Bool) {

    // this method is an extension of the UIViewController 
    // so using self works as you might expect.
    self.automaticallyAdjustsScrollViewInsets = false

    // Default is "true" so this sets it to false tells it to use 
    // the storyboard as you have it placed
    // and not how it thinks it should place it.
  }

}

My Problem: Auto Adjust set to true by default causing a difference between storyboard design and simulator Auto Adjust set to true by default causing a difference between storyboard design and simulator

Resolved: Code above applied, turning off the auto-adjust. Code above applied, turning off the auto-adjust.

Solution 4

I solved this problem by adding this line, but my problem was related to a UIView, not UIScrollView

self.navigationController.navigationBar.translucent = NO;

Solution 5

Just bare in mind that automaticallyAdjustsScrollViewInsets property works only if some kind of scroll view (table view, collection view,...) is either

  • The view of VC, or
  • First subview of this view

Other suggested, that it doest work even if it is the first subview, but there are other scroll views in the view hierarchy.

EDIT (extension DIY)

If you want similar behaviour even if you can't fulfil these conditions (e.g. you have a background image below the scroll view), you can adjust the scroll view insets manually. But please don't set it to constant like 44 or 64 or even 20 like many suggest around SO. You can't know the size ever. There can be the incall/gps/audio notification, navigation bar doesn't have to be always 44 pts etc.

I think the best solution is to use layoutGuide length in didLayoutSubviews:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    scrollView.contentInset = UIEdgeInsets(top: topLayoutGuide.length, left: 0, bottom: 0, right: 0)
    scrollView.scrollIndicatorInsets = scrollView.contentInset
}

You can use the bottomLayoutGuide in the same way.

Share:
83,278

Related videos on Youtube

user1010819
Author by

user1010819

Updated on July 23, 2020

Comments

  • user1010819
    user1010819 almost 4 years

    I have been reading a lot about iOS7 UI transition.

    I am not able to get what these three properties automaticallyAdjustsScrollViewInsets, extendedLayoutIncludesOpaqueBars, edgesForExtendedLayout??

    For example I am trying to make my view controllers start below the status bar but I am not able to achieve it.

    • user1010819
      user1010819 over 10 years
      and why would you think I have not read that?.. The problem is that is not working as expected as in I have not understood it well!!..thats the reason
    • erdekhayser
      erdekhayser over 10 years
      Ohhh I misunderstood what you were saying there. I was not trying to sound patronizing, it is just that when some people ask questions like this, and are new to the site, they miss the obvious solutions.
    • Nandha
      Nandha over 10 years
      In your viewDidLoad method, add if([self respondsToSelector:@selector(edgesForExtendedLayout)]) self.edgesForExtendedLayout = UIRectEdgeNone; which will force your view to start below the status bar.
  • Ali Beadle
    Ali Beadle over 10 years
    OK. Then I think what Nandha suggested above will achieve the same result programmatically. If it doesn't work under viewDidLoad it may need to be moved to viewDidLayoutSubviews.
  • Abdul Yasin
    Abdul Yasin over 10 years
    Indicates whether or not the extended layout includes opaque bars. @property(nonatomic, assign) BOOL extendedLayoutIncludesOpaqueBars Discussion Default value is NO.
  • Hemang
    Hemang over 10 years
    We need to set this properties only if, we're showing default navigation bar, and not if its customized navigation bar, right?
  • Antonio MG
    Antonio MG over 10 years
    No, if you use the iOS navigation bar but with your own design you'll need to use the same properties. If you just create your navigation bar by scratch, then those properties won't work.
  • aZtraL-EnForceR
    aZtraL-EnForceR over 10 years
    I dont get why there comes a small blackish layer over the entire view? Please help? :/
  • Rivera
    Rivera over 10 years
    Two questions: 1) This only works if the scroll view is the controller's view and not a subview right? 2) What to do in such case.
  • Nikita Took
    Nikita Took about 10 years
    @AntonioMG Thanks for super clear answer! But what should I do in case of storyboard? where should top edge of tableView go (to TopLayoutGuide or to top of screen)? I would like to have same behavior like last screenshot and iOs6 compability. Thanks in advance!
  • Fattie
    Fattie almost 10 years
  • Sam
    Sam almost 10 years
    This explanation is lot better than the iOS 7 UI Transition Guide. This should just replace the guide, which made no sense to me.
  • Sam
    Sam almost 10 years
    @AntonioMG Does any of these settings apply to view that has a UIToolBar at the bottom of the screen? I want the view to extend all the way to the bottom but with proper content inset such that the content is stopped where the tool bar begins.
  • Nava Carmon
    Nava Carmon over 9 years
    Does it matter if we change this property after the controller is already visible? Or we should do it on the creation? I see that changing it on fly doesn't really work
  • selva
    selva over 9 years
    +1, You gave me a hint to check in Storyboard. And i resolved my half of my problem.
  • Petar
    Petar over 9 years
    I have the same question as @BergP and would really like to find out a solution for it...
  • xmkevinchen
    xmkevinchen about 9 years
    if you turn on the automaticallyAdjustsScrollViewInsets as YES, you should set the top constraint to superview not the topGuideLayout.
  • LiangWang
    LiangWang about 9 years
    @AntonioMG, my problem is the view will still overlap the status bar if I [self.navigationController setNavigationBarHidden:YES] in ViewDidLoad.
  • Pauls
    Pauls about 9 years
    Great response, very well explained. Responding @Jacky, by default iOS grabs the view at index 0 and if its a UIScrollView, applies the properties automaticallyAdjustsScrollViewInsets to it etc. So you just have to make sure the first subview in your controllers's view is the scrollview you want to start below navbar.
  • Vladimír Slavík
    Vladimír Slavík almost 9 years
    I think extendedLayoutIncludesOpaqueBars should be YES by default instead of NO. Changing view colours (translucency) should not affect the layout.
  • Mark A. Donohoe
    Mark A. Donohoe about 7 years
    While this is an excellent answer, a caveat should be called out that these all do nothing when you drag a UITableViewController directly out onto a storyboard (i.e. not embedded in a navigation controller, etc.) I found that out thanks to this... stackoverflow.com/questions/18900428/…
  • Vojta Rujbr
    Vojta Rujbr about 7 years
    And obviously the navigation bar is translucent.
  • user3752049
    user3752049 about 6 years
    Firstly thank you for such fine explanation. I'm stuck in a mixed scenario though: I have to use opaque navigation bars (long story) and so I have to set extendedLayoutIncludesOpaqueBars to true. Also, I have to set navigationController.view.backgroundColor to avoid weird colors during back and forth transitions in a tableview. Unfortunately though navigationController.view.backgroundColor doesn't seem to work with extendedLayoutIncludesOpaqueBars. Any idea how to make this work ? *sorry for the long question
  • Harjot Singh
    Harjot Singh almost 6 years
    Adding to it, here are the new updated properties under iOS 11 which help to overcome issues related to it. stackoverflow.com/questions/45573829/…