Determine the correct size in loadView
Asked Answered
P

4

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.

Philoprogenitive answered 20/9, 2011 at 13:42 Comment(1)
Why not use auto resizing masks?Landa
T
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

Tinner answered 6/9, 2013 at 23:26 Comment(2)
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.Gnomic
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.Tinner
A
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.

Allanite answered 20/9, 2011 at 16:5 Comment(12)
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.Philoprogenitive
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.Allanite
downvote! My example is from Apples view controller programming guide on how to create your view programmatically in loadView !Allanite
I guess your first suggestion was wrong, and your current approach just cites the solution that was already included in the question.Coeval
- (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?Philoprogenitive
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.Allanite
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....Allanite
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?Philoprogenitive
Any approach that uses applicationFrame breaks down, for example, on the iPad if the view controller is presented with the FormSheet modal presentation style.Roast
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.Saito
@Allanite @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.Roentgenotherapy
You should use UIScreen anyways now with slid-over and split screen apps.Honebein
R
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.)

Roast answered 4/11, 2011 at 18:13 Comment(0)
S
-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.

Shudder answered 20/9, 2011 at 14:32 Comment(1)
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.Philoprogenitive

© 2022 - 2024 — McMap. All rights reserved.