23

How to consistently determine the correct size to layout your view in loadView?

I have different cases where a UIViewController is pushed to a UINavigationController, or a UITabBarController is present, or both. These elements changes the available screen area and makes it inconsistent if I hard code values.

At the moment where loadView is called and I want to know how to size the container view I add my subviews to.

I looked through the UIViewController guide and Apple uses UIScreen mainScreen applicationFrame, but in their example this is done because they are making a full screen app.

I have seen some answers in here, but none that addresses how to do this in a consistent manner.

Thanks for any help given.

RickiG
  • 11,380
  • 13
  • 81
  • 120

4 Answers4

16

You don't need to know the exact size. A VC shouldn't have to size itself, that's the responsibility of its parent. Just create a UIView with [[UIView alloc] init] as confirmed by Apple UIKit Engineer Andy Matuschak on Twitter: https://twitter.com/andy_matuschak/status/365227892151558144

Javier Soto
  • 4,840
  • 4
  • 26
  • 46
  • 3
    This is correct and my previous answer (now deleted) was actually wrong. You can also use `self.view = [[UIView alloc] initWithFrame:CGRectZero];`. And don't forget to set `self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;` if you don't use AutoLayout. – Tafkadasoh Sep 30 '13 at 13:11
  • autoresizing masks will actually not work if the original frame size is 0, because 0 * x = 0 :). The key is that you don't need to resize the view controller's size: the parent VC (or UIWindow if it's the main one) will do this for you. – Javier Soto Sep 30 '13 at 22:52
13
- (void) loadView {
     //self.wantsFullScreenLayout = YES; //could add this for translucent status bars
     UIView *view = [[[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame] autorelease];
     self.view = view;
}

From Apple's View Controller Programming Guide for creating view programmatically:

  1. Create a root view object that is sized to fit the screen. The root view acts as the container for all other views associated with your view controller. You typically define the frame for this view to match the size of the application window, which itself should fill the screen. However, the view controller also adjusts the frame size as needed to accommodate the presence of assorted views, such as the system status bar, a navigation bar, or a tab bar. You can use a generic UIView object, a custom view you define, or any other view that can scale to fill the screen.

Then it has an example which is similar to the one I've written above.

Ash Furrow
  • 12,391
  • 3
  • 57
  • 92
bandejapaisa
  • 26,576
  • 13
  • 94
  • 112
  • 2
    The viewController has no view property in loadView - if I call [super loadView] I will get one made, but the idea is to make my own. – RickiG Sep 20 '11 at 17:12
  • Don't call [super loadView] in your implementation of loadView. See my updated answer with my original answer removed. You create your view and then assign it to the self.view property. It should size to fit. – bandejapaisa Sep 20 '11 at 18:05
  • downvote! My example is from Apples view controller programming guide on how to create your view programmatically in loadView ! – bandejapaisa Sep 20 '11 at 20:59
  • I guess your first suggestion was wrong, and your current approach just cites the solution that was already included in the question. – Eiko Sep 21 '11 at 17:17
  • 1
    - (void) loadView { UIView *containerView = [[[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame] autorelease]; NSLog(@"%@", NSStringFromCGRect(containerView.frame)); self.view = containerView; } Trying the above when the viewController is loaded into a tabBarController logs {{0, 20}, {320, 460}} to the screen, which is not the available content size. This is what worries me - Apple recommends this approach, there are 10+ questions in here ... no one knows how to do it? – RickiG Sep 23 '11 at 12:45
  • Yeah, the API doc for applicationFrame reads "This property contains the screen bounds minus the area occupied by the status bar, if it is visible. Using this property is the recommended way to retrieve your application’s initial window size. The rectangle is specified in points." How strange. – bandejapaisa Sep 23 '11 at 13:43
  • 1
    I've created a sample application and been looking into this more. I don't think what you want, you can get out of the box (you probably know this already). UITabBarController and UINavBarController are just specialised view controllers. loadView: knows nothing about these and it shouldn't - as a UIViewController shouldn't be coupled to any specialised versions. UIScreen also shouldn't know or be coupled to UIViewControllers. When you do this in IB, you still have to size your UIView's correctly to fit within a UITabBarController or UINavigationController context.... – bandejapaisa Sep 23 '11 at 14:01
  • I just did a test to be sure as well, if I set up a view in IB and set it to "scale to fill" then tell it that it will be displayed with both a navigation bar and a tabbar - But, at run time I actually only load it with a navigationbar, it still displays the view filling out the screen nicely. Seems Apple knows the size on the content area when loading the NIB? – RickiG Sep 25 '11 at 20:00
  • Any approach that uses applicationFrame breaks down, for example, on the iPad if the view controller is presented with the FormSheet modal presentation style. – tomwhipple Nov 04 '11 at 17:47
  • Although I realize Apple provides an example that does this, I think the use of initWithFrame at all for the self.view is incorrect. You want to just call init, and trust the parent view (or the root view controller) to set the size of the view controller as a whole. After all, you have different frame dimensions in landscape and portrait. – Ben Wheeler Jun 21 '13 at 18:07
  • @bandejapaisa @property(nonatomic,retain) UIView *view; // The getter first invokes [self loadView] if the view hasn't been set yet. Subclasses must call super if they override the setter or getter. - (void)loadView; // This is where subclasses should create their custom view hierarchy if they aren't using a nib. Should never be called directly. – Dmitrii Cooler May 21 '14 at 10:55
  • You should use UIScreen anyways now with slid-over and split screen apps. – Steve Moser Jul 01 '16 at 15:10
0

The "right" answer is probably to create a custom UIView subclass and do all of the subview view positioning in your override of layoutSubviews. Your subclass would then get assigned to self.view in loadView.

However, it also works to use viewDidLoad to add a subview of identical size to self.view. (Though you should be prepared for this view to change size. -- Correctly setting autoresizingMasks can be a good way to do this.)

tomwhipple
  • 2,850
  • 27
  • 28
-1

I have made an app, it loads several UITextViews, UIButtons, and a UISegment control. What I did in viewWillAppear, I call a resize method I made.

It changes the sizes of all subviews in the UIViewController programmatically. This is because autoresizing in IB is only so good.

In my calcViewSize function, I get the dimensions like this;

CGFloat width = [[UIScreen mainScreen] applicationFrame].size.width;
CGFloat height = self.view.frame.size.height; //this will retrieve the frame we are drawn into.

Then I walk through each subview in the UIViewController that I want to resize, and change its frame. I have properties for these views in IBOutlets, so I already know what the views are.

Use Interface Builder to get the dimensions.

One last Note: To change dimensions in IB, select the View, select attributes inspector, disable the Status Bar, now in Size Inspector, you can resize the view, re-arrange your subviews, and write down the numbers to place in your code.

  • Hi eSpecialized, thanks for the input. My issue is more that I don't use IB for these views but instead lay them out in loadView. If I use applicationFrame I have to test for the presence of either a tab bar or a navigationbar and calculate my view from that. That is pretty messy to have to do for each viewController pushed to a container controller. – RickiG Sep 20 '11 at 14:38