iphone's rotated view doesn't take up whole screen - iphone

I rotated a view using CGAffineTransformMakeRotation. ( iPhone - allow landscape orientation on just one viewcontroller )
As you can see below, the images have white region in left and right.
I want the image take up the whole space with black background.(at least in one dimension, width or height )
Below is the full code
- (void) viewDidLoad
{
[super viewDidLoad];
self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
self.view.backgroundColor = [UIColor blackColor];
self.imageView.backgroundColor = [UIColor blackColor];
self.imageView.opaque = NO;
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
self.imageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
[self.imageView setImageWithURL:[NSURL URLWithString:self.jsonAlbumImage.url_image
relativeToURL: [NSURL URLWithString:#URL_BASE]]
placeholderImage: [GlobalHelper placeHolderImage]];
[self.view addSubview: self.imageView];
}
- (void)didRotate:(NSNotification *)notification {
UIDeviceOrientation orientation = [[notification object] orientation];
if (orientation == UIDeviceOrientationLandscapeLeft) {
[self.view setTransform:CGAffineTransformMakeRotation(M_PI / 2.0)];
} else if (orientation == UIDeviceOrientationLandscapeRight) {
[self.view setTransform:CGAffineTransformMakeRotation(M_PI / -2.0)];
} else if (orientation == UIDeviceOrientationPortraitUpsideDown) {
[self.view setTransform:CGAffineTransformMakeRotation(M_PI)];
} else if (orientation == UIDeviceOrientationPortrait) {
[self.view setTransform:CGAffineTransformMakeRotation(0.0)];
}
}
-- EDIT --
What worked for me in the end .. don't know why modification does work.. any explanation would be great!
UIDeviceOrientation orientation = [[notification object] orientation];
CGAffineTransform t;
CGRect rect;
if (orientation == UIDeviceOrientationLandscapeLeft) {
t = CGAffineTransformMakeRotation(M_PI / 2.0);
rect = CGRectMake(0,0,480,320);
} else if (orientation == UIDeviceOrientationLandscapeRight) {
t = CGAffineTransformMakeRotation(M_PI / -2.0);
rect = CGRectMake(0,0,480,320);
} else if (orientation == UIDeviceOrientationPortraitUpsideDown) {
t = CGAffineTransformMakeRotation(M_PI);
rect = CGRectMake(0,0,320,480);
} else if (orientation == UIDeviceOrientationPortrait) {
t = CGAffineTransformMakeRotation(0.0);
rect = CGRectMake(0,0,320,480);
}
else
return; // looks like there are other orientations than the specified 4
[self.view setTransform:t];
self.view.bounds = rect;

In - (void)didRotate:(NSNotification *)notification, you need to relayout your views so that they occupy all the space available. You could do something like this:
- (void)didRotate:(NSNotification *)notification {
UIDeviceOrientation orientation = [[notification object] orientation];
CGAffineTransform t;
if (orientation == UIDeviceOrientationLandscapeLeft) {
t = CGAffineTransformMakeRotation(M_PI / 2.0);
} else if (orientation == UIDeviceOrientationLandscapeRight) {
t = CGAffineTransformMakeRotation(M_PI / -2.0);
} else if (orientation == UIDeviceOrientationPortraitUpsideDown) {
t = CGAffineTransformMakeRotation(M_PI);
} else if (orientation == UIDeviceOrientationPortrait) {
t = CGAffineTransformMakeRotation(0.0);
}
CGPoint screenCenter = CGPointMake([UIScreen mainScreen].bounds.width/2,[UIScreen mainScreen].bounds.height/2);
self.view.center = CGPointApplyAffineTransform(screenCenter, t);
self.view.bounds = CGRectApplyAffineTransform([UIScreen mainScreen].bounds, t);
[self.view setTransform:t];
}
Where :
CGRectApplyAffineTransform
Applies an affine transform to a rectangle.
CGRect CGRectApplyAffineTransform (
CGRect rect,
CGAffineTransform t
);
Try also removing this line:
self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
You don´t need it if you are not going to use autorotation and I fear it might conflict with your own setting the view's frame. This is just an hypothesis to account for the fact that you are not seeing the view´s frame change.
EDIT:
I'd very much like to know what this transform thing does
well, the transform is doing the rotation.
rotating is relative to an anchor point which is used as a pivot; what happens is that if the anchor point is not in the middle of the view being rotated, then the view is also translated (imagine a rotation around a vertex).
So, it is correct to set the bounds to make things even; indeed I was suggesting just that with the lines:
self.view.center = CGPointApplyAffineTransform(screenCenter, t);
self.view.bounds = CGRectApplyAffineTransform([UIScreen mainScreen].bounds, t);
but possibly the idea of applying the same transform to both the center and the bounds was not blessed. (that is also why I asked for some traces, to see what was happening :-)

Related

iOS Hiding tab bar in iOS 6 creates black bar (fix for iOS 6 breaks iOS 7!)

I've got a tabbed application and in one tab there is a UIWebView. When I rotate the device to landscape I've made the UIWebView full screen while hiding the status and tab bar.
I've got it working in iOS 6 - originally when rotating and hiding the tab bar it would leave a black space where the tab bar was, so the fHeight code fixes this. However, on iOS 6 it worked perfectly, but now it actually creates the black bar problem iOS 6 was having!! Any ideas for a workaround to this?
Please see my edit below this
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
{
if(toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {
[self hideTabBar:self.tabBarController];
[[UIApplication sharedApplication] setStatusBarHidden:TRUE withAnimation:UIStatusBarAnimationSlide];
}
else
{
[self showTabBar:self.tabBarController];
[[UIApplication sharedApplication] setStatusBarHidden:FALSE withAnimation:UIStatusBarAnimationSlide];
}
}
- (void) hideTabBar:(UITabBarController *) tabbarcontroller
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
float fHeight = screenRect.size.height;
if( UIDeviceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation) )
{
fHeight = screenRect.size.width;
}
for(UIView *view in self.tabBarController.view.subviews)
{
if([view isKindOfClass:[UITabBar class]])
{
[view setFrame:CGRectMake(view.frame.origin.x, fHeight, view.frame.size.width, view.frame.size.height)];
}
else
{
[view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, fHeight)];
view.backgroundColor = [UIColor blackColor];
}
}
[UIView commitAnimations];
}
- (void) showTabBar:(UITabBarController *) tabbarcontroller
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
float fHeight = screenRect.size.height - 49.0;
if( UIDeviceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation) )
{
fHeight = screenRect.size.width - 49.0;
}
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
for(UIView *view in tabbarcontroller.view.subviews)
{
if([view isKindOfClass:[UITabBar class]])
{
[view setFrame:CGRectMake(view.frame.origin.x, fHeight, view.frame.size.width, view.frame.size.height)];
}
else
{
[view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, fHeight)];
}
}
[UIView commitAnimations];
}
//Edit
I've tried using this but I'm not sure how to pass in the view properly - I've tried self.view and webView and others but I can't get it to work on both iOS 6 and 7! Any kind of idea at all would be really helpful! Let me know if you need more info
- (void)setTabBarHidden:(BOOL)hidden view:(UIView *)view animated:(BOOL)animated
{
if (self.tabBar.hidden == hidden)
return;
CGRect screenRect = [[UIScreen mainScreen] bounds];
float height = 0.0f;
if(UIDeviceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation))
{
height = screenRect.size.width;
}
else
{
height = screenRect.size.height;
}
if (!hidden)
{
height -= CGRectGetHeight(self.tabBar.frame);
}
void (^workerBlock)() = ^() {
self.tabBar.frame = CGRectMake(CGRectGetMinX(self.tabBar.frame), height, CGRectGetWidth(self.tabBar.frame), CGRectGetHeight(self.tabBar.frame));
view.frame = CGRectMake(CGRectGetMinX(view.frame), CGRectGetMinY(view.frame), CGRectGetWidth(view.frame), height);
};
void (^completionBlock)(BOOL finished) = ^(BOOL finished) {
self.tabBar.hidden = hidden;
};
if (animated)
{
[UIView animateWithDuration:0.25f animations:workerBlock completion:completionBlock];
}
else
{
workerBlock();
completionBlock(YES);
}
}
I've created a a public Gist on Github for how we're doing this.
This solution has gone through several iterations due to #Chris Byatt and our team trying it out. So, make sure you download the latest revision from there.
The method signature has been simplified to
- (void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated;
You can call it like this within your UIViewController subclass:
[self.tabBarController setTabBarHidden:YES animated:YES];
OK guys, after struggling with this for about two hours, my colleague taught me the right way to do it. There is actually a very simple fix that I can't find online:
just put :
self.hidesBottomBarWhenPushed = YES;
In the init method of your viewController and comment out the self.tabBar.hidden related code.
I eventually got this working, but it took a while. It stemmed from a mixture of my original code and some of JRG-Developers work.
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
BOOL toLandscape = UIDeviceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation);
CGRect screenRect = [[UIScreen mainScreen] bounds];
void (^workerBlock)() = ^() {
[[UIApplication sharedApplication] setStatusBarHidden:toLandscape withAnimation:UIStatusBarAnimationSlide];
float height = toLandscape ? screenRect.size.width : screenRect.size.height - CGRectGetHeight(self.tabBarController.tabBar.frame);
float width = toLandscape ? screenRect.size.height : screenRect.size.width;
webView.frame = CGRectMake(CGRectGetMinX(webView.frame),
CGRectGetMinY(webView.frame),
width,
height);
[self moveTabBarToPosition:height];
};
[UIView animateWithDuration:0.25f animations:workerBlock];
}
//Moving the tab bar and its subviews offscreen so that top is at position y
-(void)moveTabBarToPosition:(int)y {
self.tabBarController.tabBar.frame = CGRectMake(self.tabBarController.tabBar.frame.origin.x, y, self.tabBarController.tabBar.frame.size.width, self.tabBarController.tabBar.frame.size.height);
for(UIView *view in self.tabBarController.view.subviews) {
if ([view isKindOfClass:[UITabBar class]]) {
[view setFrame:CGRectMake(view.frame.origin.x, y, view.frame.size.width, view.frame.size.height)];
} else {
[view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, y)];
view.backgroundColor = [UIColor blackColor];
}
}
}
In my case this is for my webview but theoretically you can give it any view. Works in iOS 6 and 7
-(void) hideBottomTabs{
// Get the size of the main screen
CGRect fullScreenRect = [[UIScreen mainScreen]bounds];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0){
UITabBar *bar = ((UITabBarController *)self.parentViewController).tabBarController.tabBar;
fullScreenRect.size.height += ViewHeight(bar);
}
// Hide the tab bar
((UITabBarController *)self.parentViewController).tabBarController.tabBar.hidden = YES;
// Resize and fill the screen
[[((UITabBarController *)self.parentViewController).view.subviews objectAtIndex:0] setFrame:fullScreenRect];
}
I have solved my problem in a such way.

What is the proper way to handle image rotations for iphone/ipad?

I have 2 images, one in portrait mode, and the other in landscape mode.
What is the best way to switch these images when a mobile device view rotation takes place?
Currently I just display the portrait image. And when the device rotates to landscape mode the portrait image is simply stretched.
Should I be checking within the orientation rotation handler and simply reset the image to the proper orientational image (i.e. set it manually based on the orientation)??
Thanks!
I found three ways.I think the last one is better
1: Autoresizing
Example:
UIImageView *myImageView=[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"yourImage.png"]];
myImageView.frame = self.view.bounds;
myImageView.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight
myImageView.contentMode = UIViewContentModeScaleAspectFill;
[self.view addSubview:myImageView];
[imageView release];
2: CGAffineTransformMakeRotation
Example:
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration {
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
myImageView.transform = CGAffineTransformMakeRotation(M_PI / 2);
}
else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight){
myImageView.transform = CGAffineTransformMakeRotation(-M_PI / 2);
}
else {
myImageView.transform = CGAffineTransformMakeRotation(0.0);
}
}
3:Set autosizing of myImageView as auto fill the screen in Interface Builder
Example:
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
if((self.interfaceOrientation == UIDeviceOrientationLandscapeLeft) || (self.interfaceOrientation == UIDeviceOrientationLandscapeRight)){
    myImageView.image = [UIImage imageNamed:#"myImage-landscape.png"];
} else  if((self.interfaceOrientation == UIDeviceOrientationPortrait) || (self.interfaceOrientation == UIDeviceOrientationPortraitUpsideDown)){
    myImageView.image = [UIImage imageNamed:#"myImage-portrait.png"];
} }
see more solutions here
developer.apple solution is here

Only having one view autorotate in xcode?

Ok so I currently have 3 views and I need only one of them to autorotate to any orientation while the rest stay in portrait. Right now my set up is a splashviewcontroller fades into view A, and inside view A is a button to switch to view B. All I want is for view B to be able to rotate to any orientation.
When I return YES for shouldautorotatetointerfaceorientation in the splashviewcontroller, every view rotates because this is the parent view. When I return portrait only in the splashview, nothing rotates even if the other views return YES. Is there a way to only have view B rotate? I'm willing to do it manually if you can provide code. Thanks
You can manually mange the rotation of any desired UIView object like so:
EDIT:
In the init or viewDidLoad
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(rotate) name:UIDeviceOrientationDidChangeNotification object:nil];
[super viewDidLoad];
}
#define degreesToRadian(x) (M_PI * (x) / 180.0)
-(void)rotate{
self.view.bounds = CGRectMake(0, 0, 0, 0);
if ([UIDevice currentDevice].orientation == UIInterfaceOrientationPortrait){
CGAffineTransform landscapeTransform = CGAffineTransformMakeRotation(degreesToRadian(0));
landscapeTransform = CGAffineTransformTranslate (landscapeTransform, 0.0, 0.0);
self.view.bounds = CGRectMake(self.view.bounds.origin.x, self.view.bounds.origin.y, 320, 480);
[self.view setTransform:landscapeTransform];
} else if ([UIDevice currentDevice].orientation == UIInterfaceOrientationPortraitUpsideDown){
CGAffineTransform landscapeTransform = CGAffineTransformMakeRotation(degreesToRadian(180));
landscapeTransform = CGAffineTransformTranslate (landscapeTransform, 0.0, 0.0);
self.view.bounds = CGRectMake(self.view.bounds.origin.x, self.view.bounds.origin.y, 320, 480);
[self.view setTransform:landscapeTransform];
} else if ([UIDevice currentDevice].orientation == UIInterfaceOrientationLandscapeRight){
CGAffineTransform landscapeTransform = CGAffineTransformMakeRotation(degreesToRadian(90));
landscapeTransform = CGAffineTransformTranslate (landscapeTransform, 0.0, 0.0);
self.view.bounds = CGRectMake(self.view.bounds.origin.x, self.view.bounds.origin.y, 480, 320);
[self.view setTransform:landscapeTransform];
}else if ([UIDevice currentDevice].orientation == UIInterfaceOrientationLandscapeLeft){
CGAffineTransform landscapeTransform = CGAffineTransformMakeRotation(degreesToRadian(270));
landscapeTransform = CGAffineTransformTranslate (landscapeTransform, 0.0, 0.0);
self.view.bounds = CGRectMake(self.view.bounds.origin.x, self.view.bounds.origin.y, 480, 320);
[self.view setTransform:landscapeTransform];
}
}
Inside targets summary section, Supported Interface Orientation all items are selected except for Upside Down, so all you need to do is go to your .m file that handles the view and use this piece of code.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
This did the trick for me.

How to set the different image view frame size in Orientations in iPhone?

I have created the the image view and set the images in that view and added the image view as sub view of the scroll view(like slide show images). SO i want to set the image view frame size and scroll view frame size as dynamically. I want to set the different image view frame size and the scroll view size in the portrait and landscape mode. So how can i set the different frame size in the landscape and portrait mode.
Here my sample code,
- (void)viewDidLoad {
[super viewDidLoad];
self.scroll = [[UIScrollView alloc] init];
self.scroll.delegate = self;
self.scroll.pagingEnabled = YES;
self.scroll.contentMode = UIViewContentModeScaleAspectFit;
self.scroll.autoresizingMask = ( UIViewAutoresizingFlexibleHeight |
UIViewAutoresizingFlexibleWidth );
[self.view addSubview:self.scroll];
totalArray = [[NSMutableArray alloc] initWithObjects:[UIImage imageNamed:#"dashboard_demo_2.png"],
[UIImage imageNamed:#"dashboard_demo_1.png"],
[UIImage imageNamed:#"dashboard_demo_2.png"],
[UIImage imageNamed:#"3.jpg"],
[UIImage imageNamed:#"4.jpg"],
[UIImage imageNamed:#"12.jpg"],
[UIImage imageNamed:#"5.jpg"],nil];
x = 0;
for(int i = 0; i< [totalArray count]; i++)
{
img = [[UIImageView alloc] initWithFrame:CGRectMake(x, 0, 320, 200)];
img.image =[totalArray objectAtIndex:i];
x = x+320; // in portait mode if the image sets correctly, but landscape mode it will x = x + 480;
[self.scroll addSubview:img];
}
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientationPortrait ||
orientation == UIDeviceOrientationPortraitUpsideDown )
{
[self portraitMode];
}
else if (orientation == UIDeviceOrientationLandscapeLeft ||
orientation == UIDeviceOrientationLandscapeRight )
{
[self LandscapeMode];
}
return YES;
}
-(void) portraitMode
{
//How to set the frame sizes.
self.scroll.frame = CGRectMake(0, 0, 320, 200);
img.frame = CGRectMake(x, 0, 320, 200);
scroll.contentSize = CGSizeMake(x, 200); // it doesn't set
}
-(void) LandscapeMode
{
//How to set the frame sizes
self.scroll.frame = CGRectMake(0, 0, 480, 320);
img.frame = CGRectMake(x, 0, 480, 200);
scroll.contentSize = CGSizeMake(x, 320); // it doesn't set
}
PLease help me out.
Thanks.
Hi ,
you can register to notification center for change in screen orientation.
Code to enroll with notification center.
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(ScreenRotate:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
Implement the ScreenRotate method.
-(void) screenRotate:(NSNotificationCenter*) notification
{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if( orientation == UIDeviceOrientationLandscapeLeft)
{
}
else if ( orientation == UIDeviceOrientationLandscapeRight )
{
}
else if ( orientation == UIDeviceOrientationPortrait )
{
}
}
Code to unregister with notification center.
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIDeviceOrientationDidChangeNotification
object:nil];
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}

Automatically Sizing UIView after Adding to Window

Note: This may be a duplicate of Subview Doesnt AutoSize When Added to Root View Controller
I have an iPad app that switches between different views in its main window. The view-switching code looks like this:
- (void)switchToViewController:(UIViewController*)viewController {
if (currentViewController != viewController) {
[currentViewController.view removeFromSuperview];
currentViewController = viewController;
[window addSubview:viewController.view];
}
}
The problem is that when the new view (a UISplitView) appears in landscape orientation, it is not sized to fill the entire window. There is a large empty black space on the right. It looks like the view is only 768 pixels wide, rather than the 1024-pixel width of the landscape window.
If I rotate the device to portrait and then back to landscape, the view sizes itself properly.
If the device is in portrait orientation, everything works fine. The UISplitView also gets sized properly if it is the first view I show. The problem only occurs if I switch to it after another view has been shown, in landscape.
So, is there some way to force iPhone OS to resize the view after it has been added to the window?
I've tried calling sizeToFit, and setNeedsLayout. I've also tried setting the view's bounds to the window's bounds, and I've tried setting the frame to match the previous view's frame.
This is absolutely possible! :-)
You can check out my repo here:
https://github.com/hfossli/AGWindowView
It will automatically deal with any rotation and framechanges so you won't have to worry about that.
If you like to worry about that then you can just cut and paste the most important parts
# 1 Add view to window
[[UIApplication sharedApplication] keyWindow] addSubview:aView];
# 2 Add listener and update view
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(statusBarFrameOrOrientationChanged:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(statusBarFrameOrOrientationChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
Remember to remove notification listening
[[NSNotificationCenter defaultCenter] removeObserver:self];
# 3 Do the math
- (void)statusBarFrameOrOrientationChanged:(NSNotification *)notification
{
/*
This notification is most likely triggered inside an animation block,
therefore no animation is needed to perform this nice transition.
*/
[self rotateAccordingToStatusBarOrientationAndSupportedOrientations];
}
- (void)rotateAccordingToStatusBarOrientationAndSupportedOrientations
{
UIInterfaceOrientation statusBarOrientation = [UIApplication sharedApplication].statusBarOrientation;
CGFloat angle = UIInterfaceOrientationAngleOfOrientation(statusBarOrientation);
CGFloat statusBarHeight = [[self class] getStatusBarHeight];
CGAffineTransform transform = CGAffineTransformMakeRotation(angle);
CGRect frame = [[self class] rectInWindowBounds:self.window.bounds statusBarOrientation:statusBarOrientation statusBarHeight:statusBarHeight];
[self setIfNotEqualTransform:transform frame:frame];
}
- (void)setIfNotEqualTransform:(CGAffineTransform)transform frame:(CGRect)frame
{
if(!CGAffineTransformEqualToTransform(self.transform, transform))
{
self.transform = transform;
}
if(!CGRectEqualToRect(self.frame, frame))
{
self.frame = frame;
}
}
+ (CGFloat)getStatusBarHeight
{
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if(UIInterfaceOrientationIsLandscape(orientation))
{
return [UIApplication sharedApplication].statusBarFrame.size.width;
}
else
{
return [UIApplication sharedApplication].statusBarFrame.size.height;
}
}
+ (CGRect)rectInWindowBounds:(CGRect)windowBounds statusBarOrientation:(UIInterfaceOrientation)statusBarOrientation statusBarHeight:(CGFloat)statusBarHeight
{
CGRect frame = windowBounds;
frame.origin.x += statusBarOrientation == UIInterfaceOrientationLandscapeLeft ? statusBarHeight : 0;
frame.origin.y += statusBarOrientation == UIInterfaceOrientationPortrait ? statusBarHeight : 0;
frame.size.width -= UIInterfaceOrientationIsLandscape(statusBarOrientation) ? statusBarHeight : 0;
frame.size.height -= UIInterfaceOrientationIsPortrait(statusBarOrientation) ? statusBarHeight : 0;
return frame;
}
CGFloat UIInterfaceOrientationAngleOfOrientation(UIInterfaceOrientation orientation)
{
CGFloat angle;
switch (orientation)
{
case UIInterfaceOrientationPortraitUpsideDown:
angle = M_PI;
break;
case UIInterfaceOrientationLandscapeLeft:
angle = -M_PI_2;
break;
case UIInterfaceOrientationLandscapeRight:
angle = M_PI_2;
break;
default:
angle = 0.0;
break;
}
return angle;
}
UIInterfaceOrientationMask UIInterfaceOrientationMaskFromOrientation(UIInterfaceOrientation orientation)
{
return 1 << orientation;
}
Good luck!
This works, but it seems a little hacky:
- (void)switchToViewController:(UIViewController *)viewController {
if (viewController != currentViewController) {
UIInterfaceOrientation orientation = currentViewController.interfaceOrientation;
[currentViewController.view removeFromSuperview];
currentViewController = viewController;
UIView *view = viewController.view;
// Set appropriate view frame (it won't be autosized by addSubview:)
CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
if (UIInterfaceOrientationIsLandscape(orientation)) {
// Need to flip the X-Y coordinates for landscape
view.frame = CGRectMake(appFrame.origin.y, appFrame.origin.x, appFrame.size.height, appFrame.size.width);
}
else {
view.frame = appFrame;
}
[window addSubview:view];
}
}
The window may include other UI elements besides your view. The 20 pixel difference in your example is the height of the status bar.
[[UIApplication sharedApplication] statusBarFrame].height;
Neither the window nor screen rotate. Getting their frames and using them for a rotated view will only work if you have switched the height and width.
If you are using a UIViewController, try returning YES from this method:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation; // Override to allow rotation. Default returns YES only for UIDeviceOrientationPortrait
I got the same problem, but i fixed it with this lines of code:
- (void)changeRow:(NSNotification *)notification {
[window addSubview:new.view];
[old.view removeFromSuperview];
[new.view removeFromSuperview];
[window addSubview:new.view];
}
You must add the new view, then remove the old and the new and then add the new view. I don't know why, but that works.
Fossli's answer is correct for iPad. However, I have a universal app that I needed to support. Therefore some adjustments are necessary.
Add the following to AppDelegate.h
#property (strong, nonatomic) UIImageView *imageView;
Add the following to AppDelegate.m
#synthesize imageView;
- (void)orientationChanged:(NSNotification *)notification
{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (! (UIInterfaceOrientationIsLandscape(deviceOrientation) ||
UIInterfaceOrientationIsPortrait(deviceOrientation)))
{
// May be "UIInterfaceOrientationUnknown" which does not appear to be a defined value anywhere.
return;
}
[imageView setImage:[UIImage imageNamed:[Utility getBackgroundImageNameWithOrientation:deviceOrientation]]];
/*
iOS Image Sizes
iPhone/iPod Portrait 320 x 480 (640 x 960 #2x)
iPad Portrait 768 x 1004 (1536 x 2008 #2x)
Landscape 1024 x 748 (2048 x 1496 #2x)
iPad window bounds in both orientations 768 x 1024 (needs manual swap in landscape)
iPhone window bounds in both orientations 320 x 480 (needs manual swap in landscape)
Note the size variations between the required default launch image sizes and
the size of the window bounds.
iPhone/iPod only requires rotations.
iPad needs origin or size adjustments depending on orientation.
*/
CGFloat angle = 0.0;
CGRect newFrame = [[self window] bounds];
// How to get size of status bar
// Size of status bar gets all wonky on rotations so just set it manually
// CGSize statusBarSize = [[UIApplication sharedApplication] statusBarFrame].size;
CGSize statusBarSize = CGSizeMake(20.0, 20.0);
if (deviceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
angle = M_PI;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
newFrame.size.height -= statusBarSize.height;
}
}
else if (deviceOrientation == UIInterfaceOrientationLandscapeLeft)
{
angle = - M_PI / 2.0f;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
newFrame.origin.x += statusBarSize.height;
newFrame.size.width += statusBarSize.height;
}
}
else if (deviceOrientation == UIInterfaceOrientationLandscapeRight)
{
angle = M_PI / 2.0f;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
newFrame.size.width -= statusBarSize.height;
}
}
else
{
angle = 0.0;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
newFrame.origin.y += statusBarSize.height;
newFrame.size.height -= statusBarSize.height;
}
}
imageView.transform = CGAffineTransformMakeRotation(angle);
imageView.frame = newFrame;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Add background image to window with orientation changes so that it is visible in all views.
// A listener is added since subviews do not receive orientation changes.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object: nil];
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[Utility getBackgroundImageNameWithOrientation:deviceOrientation]]];
[[self window] addSubview:imageView];
return YES;
}
Add the following to Utility.h
+ (NSString *)getBackgroundImageNameWithOrientation:(UIDeviceOrientation)interfaceOrientation;
Add the following to Utility.m
+ (NSString *)getBackgroundImageNameWithOrientation:(UIDeviceOrientation)interfaceOrientation
{
NSString *imageName = nil;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
{
imageName = #"Default-Landscape~ipad.png";
}
else
{
imageName = #"Default-Portrait~ipad.png";
}
}
else
{
if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
{
imageName = #"Default-Landscape~iphone.png";
}
else
{
imageName = #"Default.png";
}
}
return imageName;
}
Windows of iOS7 have different behaviors with windows of iOS8/9.
Keyboard window of iOS7 and all windows of iOS8/9 always have correct orientation & size. So you can observer the size change events and update the frame of your view.
But other windows of iOS7 always keep the portrait orientation and size. You need update transform of your view after rotation.
You need to observer UIApplicationWillChangeStatusBarOrientationNotification and update size of your UIView like this:
#interface MyView : UIView
#end
#implementation MyView
- (instancetype)init
{
if (self = [super init]) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(changeOrientationHandler:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
}
- (void)updateTransformWithOrientation:(UIInterfaceOrientation)orientation
{
CGFloat width = CGRectGetWidth(self.window.bounds);
CGFloat height = CGRectGetHeight(self.window.bounds);
if (width > height) {
CGFloat temp = width;
width = height;
height = temp;
}
CGFloat offset = (height - width) / 2;
CGAffineTransform transform;
switch (orientation) {
case UIInterfaceOrientationLandscapeLeft:
transform = CGAffineTransformMakeTranslation(-offset, offset);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
case UIInterfaceOrientationLandscapeRight:
transform = CGAffineTransformMakeTranslation(-offset, offset);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;
case UIInterfaceOrientationPortraitUpsideDown:
transform = CGAffineTransformMakeRotation(-M_PI);
break;
default:
transform = CGAffineTransformIdentity;
break;
}
self.transform = transform;
self.frame = CGRectMake(0, 0, width, height);
}
- (void)updateFrameWithOrientation:(UIInterfaceOrientation)orientation
{
CGFloat width = CGRectGetWidth(self.window.bounds);
CGFloat height = CGRectGetHeight(self.window.bounds);
if (width > height) {
CGFloat temp = width;
width = height;
height = temp;
}
switch (orientation) {
case UIInterfaceOrientationLandscapeLeft:
case UIInterfaceOrientationLandscapeRight:
self.frame = CGRectMake(0, 0, height, width);
break;
default:
self.frame = CGRectMake(0, 0, width, height);
break;
}
}
- (void)updateWithOrientation:(UIInterfaceOrientation)orientation
{
BOOL isIos7 = [[UIDevice currentDevice].systemVersion floatValue] < 8.0;
BOOL isKeyboardWindow = [self.window isKindOfClass:NSClassFromString(#"UITextEffectsWindow")];
if (isIos7 == YES && isKeyboardWindow == NO) {
[self updateTransformWithOrientation:orientation];
} else {
[self updateFrameWithOrientation:orientation];
}
}
- (void)changeOrientationHandler:(NSNotification *)notification
{
[UIView animateWithDuration:0.25 animations:^{
UIInterfaceOrientation orientation = (UIInterfaceOrientation)[notification.userInfo[UIApplicationStatusBarOrientationUserInfoKey] integerValue];
[self updateWithOrientation:orientation];
}];
}
#end