Question

I having problems getting an accurate reading for heights and widths. So I made this quick and dirty app to test the situation.

Here is the code:

@interface wwfpViewController ()
@property (nonatomic, strong) UIView * box;
@property (nonatomic, strong) UILabel * info;
@end

@implementation wwfpViewController
@synthesize box,info;

- (void)viewDidLoad {
    [super viewDidLoad];

    box=[[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
    [box setBackgroundColor:[UIColor darkGrayColor]];
    [box setAutoresizesSubviews:YES];
    [box setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];

    info=[[UILabel alloc] initWithFrame:CGRectMake(10, 10, 300, 300)];
    [info setTextColor:[UIColor whiteColor]];
    [info setBackgroundColor:[UIColor blackColor]];
    [info setLineBreakMode:NSLineBreakByWordWrapping];
    [info setNumberOfLines:10];
    [info setText:@"..."];

    [self.view addSubview:box];
    [box addSubview:info];

    [self updateInfo];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(updateView:)
                                                 name:UIApplicationDidChangeStatusBarOrientationNotification
                                               object:nil];

}

- (void) updateInfo {
    CGFloat selfHeight=self.view.frame.size.height;
    CGFloat selfWidth=self.view.frame.size.width;
    CGFloat boxHeight=box.frame.size.height;
    CGFloat boxWidth=box.frame.size.width;
    int deviceOrientation=[[UIDevice currentDevice] orientation];
    int statusOrientation=[[UIApplication sharedApplication] statusBarOrientation];

    NSString * str=[NSString stringWithFormat:@"[height x width] \nself: [%f x %f] \nbox: [%f x %f] \ndevice: %d status: %d",selfHeight,selfWidth,boxHeight,boxWidth,deviceOrientation,statusOrientation];

    [info setText:str];
}

- (void) updateView: (NSNotification*) notify {
    [self updateInfo];
}


@end

When I test this on an iPad, initially in portrait mode, the info label reports the following:

[height x width]
self: [1004.000000 x 768.000000]
box: [1004.000000 x 768.000000]
device: 0 status: 1

This is correct!

And then when I rotate the iPad to landscape, I get these readings:

[height x width]
self: [768.000000 x 1004.000000]
box: [1004.000000 x 768.000000]
device: 3 status: 3

Actual height x width: 748 x 1024

But when I test this on the iPad when its in landscape orientation, the info label reports:

[height x width]
self: [1024.000000 x 748.000000]
box: [1024.000000 x 748.000000]
device: 0 status: 3

Actual height x width: 748 x 1024

Then when I rotate the iPad to portrait, I get these readings:

[height x width]
self: [748.000000 x 1024.000000]
box: [748.000000 x 1024.000000]
device: 1 status: 1

Actual height x width: 1004 x 768

I rotate it back to landscape and then I get these readings:

[height x width]
self: [768.000000 x 1004.000000]
box: [1004.000000 x 768.000000]
device: 3 status: 3

Actual height x width: 748 x 1024

In all cases the box UIView covers the entire screen, so it is auto adjusting to the orientation changes correctly. These results are consistent from the simulator and testing it on an actual iPad, and I have similar experiences on an iPhone.

After this, I have a few questions:

  1. What am I doing wrong?
  2. Why is the height and width for self.view different from the height and width for box when the two look visually identical?
  3. How can I accurately obtain the overall height and width of the screen or a view, irrespective of orientation changes.

  4. Because [UIDevice ... orientation] reports as zero the first time it is used, should I just ignore it altogether and just stick with [UIApplication ... statusBarOrientation]?
Was it helpful?

Solution

Check the bounds of the views rather than the frame:

CGFloat selfHeight=self.view.bounds.size.height;
CGFloat selfWidth=self.view.bounds.size.width;
CGFloat boxHeight=box.bounds.size.height;
CGFloat boxWidth=box.bounds.size.width;

Also, I would use the UIViewController method for orientation changes and remove the NSNotificationCenter observer.

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    [self updateInfo];
}

Finally, the first call to get the correct size should be in viewWillAppear as it will be incorrect in viewDidLoad:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self updateInfo];
}

OTHER TIPS

I tried your code and found one mistake here.

Cause of bug- You didn't make app navigationController based in appdelegate. Set your window's rootViewController to navigationController don't know why but viewController's based view doesn't give the correct frame.

My Opinion- Don't use notification here when you have already a method
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation which is call after orientation changed. Well You are using notification then NP this is just my opinion.

One more thing I would like to say

box = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];

Why don't you simply do

box = [[UIView alloc] initWithFrame:self.view.bounds];

Your Code is okay. Just set your window's rootViewController to navigationController OR tabbarController.

This is Sample Code.

From Apple docs

Discussion When a view’s bounds change, that view automatically resizes its subviews according to each subview’s autoresizing mask. You specify the value of this mask by combining the constants described in UIViewAutoresizing using the C bitwise OR operator. Combining these constants lets you specify which dimensions of the view should grow or shrink relative to the superview. The default value of this property is UIViewAutoresizingNone, which indicates that the view should not be resized at all.

When more than one option along the same axis is set, the default behavior is to distribute the size difference proportionally among the flexible portions. The larger the flexible portion, relative to the other flexible portions, the more it is likely to grow. For example, suppose this property includes the UIViewAutoresizingFlexibleWidth and UIViewAutoresizingFlexibleRightMargin constants but does not include the UIViewAutoresizingFlexibleLeftMargin constant, thus indicating that the width of the view’s left margin is fixed but that the view’s width and right margin may change. Thus, the view appears anchored to the left side of its superview while both the view width and the gap to the right of the view increase.

If the autoresizing behaviors do not offer the precise layout that you need for your views, you can use a custom container view and override its layoutSubviews method to position your subviews more precisely.

It probably has to do with autoresizing masks read more about it here.

myview = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];

Working for me

I've made macros vWidth and vHeight that will handle all of this logic properly, with fallbacks:

#define sWidth [[UIScreen mainScreen] bounds].size.width
#define sHeight [[UIScreen mainScreen] bounds].size.height

#define vWidth (\
(^float (void){\
BOOL isClassMethod = [[self class] respondsToSelector:_cmd];\
if (isClassMethod) {\
return sWidth;\
} else {\
float __vWidth = ([self isKindOfClass:[UIViewController class]]?((UIViewController *)self).view.frame.size.width:((UIView *)self).frame.size.width);\
if (!__vWidth) {\
__vWidth = ([self isKindOfClass:[UIViewController class]]?((UIViewController *)self).view.superview.frame.size.width:((UIView *)self).superview.frame.size.width);\
}\
if (!__vWidth) {\
__vWidth = sWidth;\
}\
return __vWidth;\
}\
})()\
)
#define vHeight (\
(^float (void){\
BOOL isClassMethod = [[self class] respondsToSelector:_cmd];\
if (isClassMethod) {\
return sHeight;\
} else {\
float __vHeight = ([self isKindOfClass:[UIViewController class]]?((UIViewController *)self).view.frame.size.height:((UIView *)self).frame.size.height);\
if (!__vHeight) {\
__vHeight = ([self isKindOfClass:[UIViewController class]]?((UIViewController *)self).view.superview.frame.size.height:((UIView *)self).superview.frame.size.height);\
}\
if (!__vHeight) {\
__vHeight = sHeight;\
}\
return __vHeight;\
}\
})()\
)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top