Search code examples
objective-corientationdimensionscgrect

Objective C : Getting the height and width of a UIView accurately


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]?

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];
    }