Reputation: 10119
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:
self.view
different from the height and width for box
when the two look visually identical?[UIDevice ... orientation]
reports as zero the first time it is used, should I just ignore it altogether and just stick with [UIApplication ... statusBarOrientation]
?Upvotes: 2
Views: 22902
Reputation: 17882
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;\
}\
})()\
)
Upvotes: 0
Reputation: 921
myview = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
Working for me
Upvotes: 0
Reputation: 5591
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];
}
Upvotes: 10
Reputation: 13354
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.
Upvotes: 1
Reputation: 559
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.
Upvotes: 0