orientation incorrectly reported in viewDidLoad - iphone

So I have an app that needs to load a different image as the background image depending on the orientation of the device. I have the following code in viewDidLoad:
BOOL isLandScape = UIDeviceOrientationIsLandscape(self.interfaceOrientation);
if (isLandScape)
{
self.bgImage.image = [UIImage imageNamed:#"login_bg748.png"];
}
For some reason even if the simulator starts in landscape this bool is still false. I checked and it always reports being in portrait mode regardless of the actual simulator orientation. Does anyone have an idea as to why this is not working?
In shouldAutoRotateForInterfaceOrientation I have the following:
if (UIDeviceOrientationIsLandscape(interfaceOrientation))
{
self.bgImage.image = [UIImage imageNamed:#"login_bg748.png"];
} else
{
self.bgImage.image = [UIImage imageNamed:#"login_bg1004.png"];
}
return YES;
And this code does work, its just the startup that is messed up. After I perform one rotation it works fine.

The reason is that viewDidLoad is too early. Every app launches in portrait and later rotates to landscape. When viewDidLoad is called, the rotation has not happened yet. You want to use delayed performance, or put your tests in didRotateFromInterfaceOrientation or similar. See the explanation in my book:
http://www.apeth.com/iOSBook/ch19.html#_rotation

First in the functionshouldAutoRotateForInterfaceOrientation you just have to return YES
now use this function
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
{
//landscape view login
}
else
{
//portrait View logic
}
}
And if you are already in landscape view or portrait view then in your viewDidLoad function
-(void)viewDidLoad
{
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
{
//landscape view code
}
else
{
//portrait view code
}
}
hope this will help

Related

iOS7 / iOS6 Conditional Rotation Portrait / Landscape for different sections of App

Problem: A have an App that uses both Landscape mode (locked) and Portrait Mode (locked) for different parts of the app. Now I have a working solution however it doesn't seem correct and does have it's own problems.
Optimally I would love to force a orientation change. Thinking even about doing a view transformation if needed.
Basic flow of App:
HomeView (Portrait) (which has a few sub pushed views that are also portrait and locked to that).
LandscapeView (Landscape) (which has 5 pushed subviews that are also landscape)
Note:
HomeView has a link to LandscapeView
LandscapeView can go back to HomeView
At the end of the LandscapeView subviews it returns to the HomeView
Basic Image showing how this looks with the different view orientations. (The lines indicate flow of app, orientation of the images indicate how each screen should be )
Currently using the below implementation to call / set if the view is in portrait mode or landscape mode by [setLockedToPortait:YES] (for portrait view) etc.
This in term makes the query for what interface orientation to use from iOS if the device is rotated.
Now for the case of going to the LandscapeView, I show a temporary view over the top of the normal view asking to use to rotate their phone to landscape. (A temporary view is also shown when returning to the HomeView from a landscape view)
So once the user has rotated their device, it will trigger the correct orientation and then the temporary view will hide.
If the user then rotates their phone back to portrait at this point it will still be locked to landscape so will not trigger another view rotation (also no temp view will appear or anything)
Current Implementation Code::
// ---------------------- NavigationController (subclass of UINavigationController)
#interface NavigationController () {
BOOL isOrientationPortrait;
}
#end
#implementation NavigationController {
UIDeviceOrientation lastAccepted;
UIDeviceOrientation lastKnown;
}
-(void)setLockedToPortait:(BOOL)isLocked {
isOrientationPortrait = isLocked;
}
-(UIDeviceOrientation) getCurrentOrientation {
UIDeviceOrientation orientate = [[UIDevice currentDevice] orientation];
if(orientate == 0) { // needed for simulator
orientate = (UIDeviceOrientation)[UIApplication sharedApplication].statusBarOrientation;
}
return orientate;
}
// Deprecated in iOS6, still needed for iOS5 support.
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)toInterfaceOrientation
{
UIDeviceOrientation orientation = [self getCurrentOrientation];
[self setLastKnownOrientation:orientation];
if(isOrientationPortrait == YES) {
if([self isLastKnownPortrait] == YES) {
[self setLastAcceptedOrientation:orientation];
return YES;
} else {
return NO;
}
} else {
if([self isLastKnownLandscape] == YES) {
[self setLastAcceptedOrientation:orientation];
return YES;
} else {
return NO;
}
}
}
// iOS6/7 support
- (BOOL)shouldAutorotate
{
// find out the current device orientation
UIDeviceOrientation orientation = [self getCurrentOrientation];
[self setLastKnownOrientation:orientation];
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
if(isOrientationPortrait == YES) {
if([self isLastKnownPortrait] == YES)
{
UIDeviceOrientation orientation = [self getCurrentOrientation];
[self setLastAcceptedOrientation:orientation];
}
return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
} else {
if([self isLastKnownLandscape] == YES)
{
UIDeviceOrientation orientation = [self getCurrentOrientation];
[self setLastAcceptedOrientation:orientation];
}
return (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight );
}
}
-(void)setLastAcceptedOrientation:(UIDeviceOrientation)orient {
lastAccepted = orient;
}
-(void)setLastKnownOrientation:(UIDeviceOrientation)orient {
lastKnown = orient;
}
-(BOOL)isLastKnownPortrait {
return UIDeviceOrientationIsPortrait(lastKnown);
}
-(BOOL)isLastKnownLandscape {
return UIDeviceOrientationIsLandscape(lastKnown);
}
-(BOOL)isLastAcceptedPortrait {
return UIDeviceOrientationIsPortrait(lastAccepted);
}
-(BOOL)isLastAcceptedLandscape {
return UIDeviceOrientationIsLandscape(lastAccepted);
}
Current Problems:
Device rotations are always required after a view has loaded for the user going to Landscape mode from Portrait and vice versa.
If the user has the device orientation locked, this will not work at all.
When transitioning back from Landscape mode, and the user has already rotated their device to Portrait (in the last landscape view), the Portrait view's interface will be locked to a 'Landscape' layout until the user re-rotates their device (so currently I am just showing the overlay to rotate the device, but it is already rotated… very annoying for the user). Massive issue right now with the above implementation.
Would love to be able to:
Force an orientation change on the phone for the current view.
Set a preferred layout for a view which is forced between push/pops of views.
I've looked a lot at the other solutions on here and on the Apple Dev forums, however none seem to cover this problem, or still this orientation bug between the two views exists as well.
Thanks for any help or pointers! No advice will be discounted :D
--
Edit::
Solution Found thanks to #leo-natan!!
So instead of trying to force a change of orientation on the views. Just push a new modal view. This forces a change. You still need to above orientation code for managing rotations.
So what I have now in my HomeViewController:
LandscapeViewController * viewController = [[[LandscapeViewController ViewController alloc] init] autorelease];
UINib * nib = [UINib nibWithNibName:#"NavigationController" bundle:nil];
NavigationController *navController = [[nib instantiateWithOwner:nil options:nil] objectAtIndex:0];
[navController initWithRootViewController:viewController];
[self presentViewController:navController animated:YES completion:^{
// completion
}];
So it is necessary to re-add a new navigation controller for this modal view. Also note above 'presentViewController' is the new way of pushing Modal views.
Implemented this overloaded method for the managing of the view controller:
-(id)initWithRootViewController:(UIViewController *)rootViewController {
self = [super initWithRootViewController:rootViewController];
if(self){
}
return self;
}
Note: The above is not using storyboards. The problem may be solved by using storyboards and modally showing a view in the same fashion.
See my answer here, including a test project.
Basically, orientation can only be forced to change when presenting a view controller modally. For example, media playback in some apps. If you wish to transition from a view controller that can only be presented in portrait to a view controller that is only presented in landscape, you will need a modal presentation. Push will not work.

Rotation broken in iOS6

I've been trying all morning to fix this, to no avail. Here's my situation:
I have a navigation controller across two views in my app. The first view shouldn't rotate away from portrait. The second view should rotate between portrait and landscape. Going back to the first view should send it back to portrait.
Here's the code I have currently (which i've experimented with without success, so is in no way solid):
AppDelegate:
- (NSUInteger)application:(UIApplication*)application
supportedInterfaceOrientationsForWindow:(UIWindow*)window
{
return UIInterfaceOrientationMaskAll;
}
Navigation controller:
- (BOOL)shouldAutorotate
{
return self.topViewController.shouldAutorotate;
}
First view controller:
-(BOOL)shouldAutorotate
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
Second view controller:
-(BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
The current behaviour is that app will stay in portait on the first VC, rotate properly on the second VC, but if I go back while in landscape, the first VC is in landscape and stays there. How can I fix this?
I was having the same issue, and as far as I can remember, you can't fix it due to the way how auto-rotation works. However, I did come up with a workaround - when in landscape mode, I hid the navigation bar, disabling the option to tap the back button. This might or might not work for you, but if you come up with a better solution, I would love to hear it.

Making a view go portrait straight away without rotating

Currently i have this code in all my views which i don't want to go into landscape which is all except for one:
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// You do not need this method if you are not supporting earlier iOS Versions
return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
-(NSUInteger)supportedInterfaceOrientations
{
if (self.selectedViewController)
return [self.selectedViewController supportedInterfaceOrientations];
return UIInterfaceOrientationMaskPortrait;
}
-(BOOL)shouldAutorotate
{
return YES;
}
this is the code in my tab bar controller:
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// You do not need this method if you are not supporting earlier iOS Versions
return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
-(NSUInteger)supportedInterfaceOrientations
{
if (self.selectedViewController)
return [self.selectedViewController supportedInterfaceOrientations];
return UIInterfaceOrientationMaskPortrait;
}
-(BOOL)shouldAutorotate
{
return YES;
}
And lastly the code in the viewcontroller that i want to go into landscape (because there is a video):
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
The views are all rotating like i want them to, the only problem is when the viewcontroller with the video in rotates and you then click on another view that view will stay in landscape, what i want to happen is this view to rotate straight into portrait without the user rotating the device into portrait. Anyone know what code i need?
This can be a difficult one to solve elegantly. Forcing orientation can be breaking Human Interface Rules rules or in the past just required calling private API's
I would recommend attempting a different approach which has worked for me in the past. When the user rotates the one viewcontroller that displays a video allow that to rotate but at that time remove any way the user can navigate away and then return those navigation controls when they rotate the device back to portrait. For me it was simply hiding the navigation bar until they returned to portrait. I like this because it is very clear to the user that they have to return the phone to portrait before moving on and they won't be surprised with navigating somewhere else that is magically another orientation.

How to allow only particular view to rotate? (iPhone)

I am installing AdMob's ad, and there is a GADBannerView.
After installing, a banner show, and if you click on it, a page will slide out coving the whole screen, and displaying advertising contents in it.
The question is, some advertising contents, such as video, had to be played landscape. However, I don't want other part of my application to rotate, as the app is not designed to be viewed in landscape.
So, how can I implement something which can achieve such function?
Try to use Notification for this. a notification calls a selector every time when ur device orientation is changed.
write this in your viewDidLoad:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(setScreenWithDeviceOrientation:) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
and then define the selector as follows:
-(void)setScreenWithDeviceOrientation:(NSNotification *)notification
{
UIDeviceOrientation orientation=[[UIDevice currentDevice] orientation];
if(orientation==UIInterfaceOrientationPortrait) //Portrait orientation
{
// setView frame for portrait mode
}
else if(orientation==UIInterfaceOrientationPortraitUpsideDown) // PortraitUpsideDown
{
// setView frame for upside down portrait mode
}
else if(orientation==UIInterfaceOrientationLandscapeLeft)
{
// setView frame for Landscape Left mode
}
else if(orientation==UIInterfaceOrientationLandscapeRight) //landscape Right
{
// setView frame for Landscape Right mode
}
else
{
NSLog(#"No Orientation");
}
}
this method fired everytime when ur device changes orientation. Based on the current orientation you should adjust your view.
I hope this will help you.
Are you working with iOS 6? You should be able to just restrict what orientations your view controller handles in this case. For example, in your view controller that handles your GADBannerView, you can just put:
// Tell the system what we support
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
// Tell the system It should autorotate
- (BOOL) shouldAutorotate {
return NO;
}
// Tell the system which initial orientation we want to have
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
And that should make it so that your viewcontroller only supports portrait.
Have you looked at this question and answer? This explains how a single view can work in a certain orientation that is not supported by the rest of the application:
AutoRotate ONLY MpMoviePlayerControler iOS 6

issue about willAnimateRotate and didRotateFrom when doing an rotation

What I am having so far is
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
NSLog(#"willAnimateRotationToInterfaceOrientation");
if(toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
NSLog(#"PortraitUpsideDown");
// Do method A
} else {
[[self.view subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
// Do method B
}
}
and
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
NSLog(#"didRotateFromInterfaceOrientation");
if( fromInterfaceOrientation == UIInterfaceOrientationPortrait || fromInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown){
NSLog(#"OrientationPortrait or PortraitUpsideDown");
[[self.view subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
// Do method B
} else {
NSLog(#"From else");
[[self.view subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
// Do method A
}
}
My logic is after hitting the RUN from xcode, willAnimateRotationToInterfaceOrientation: is going to be called because I set Supported Device Orientation to be UpsideDown from Summary of MyApp.xcodeproj. Moreover, I also think that didRotateFromInterfaceOrientation: should not be called because we have just started the app yet. It means there are no previous states at all.
Unfortunately, this is what I got after doing the debugger
2012-02-11 12:04:08.776 MyApp[7505:10703] willAnimateRotationToInterfaceOrientation :
2012-02-11 12:04:08.776 MyApp[7505:10703] PortraitUpsideDown
2012-02-11 12:04:08.778 MyApp[7505:10703] didRotateFromInterfaceOrientation :
2012-02-11 12:04:08.779 MyApp[7505:10703] OrientationPortrait or
PortraitUpsideDown
I am getting lost now. Does anyone have any ideas about the issue, please help. Thanks.
I have had some issues with rotation, similar to yours. The short version is that the will and did methods are not 100% reliable. You should try rapidly rotating back and forth and see what happens. It could be because I am pretty new, or it could be an actual bug. What I did was, in didRotateFromInterfaceOrientation:, check the current orientation and act accordingly:
if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation) {
// do your portrait stuff
} else {
// do your landscape stuff
}
Before I found the UIInterfaceOrientationIsPortait test I was actually checking the frame to see if self.view.frame.size.width > self.view.frame.size.height, and that is also reliable but kind of ugly.
Either way is reliable. I have a couple apps in the store, QPalettes and QColor, that do rotation on a bunch of user-created onscreen elements. What I had to do is store the dimensions, and after didRotateFromInterfaceOrientation I check the new dimensions and if they are different, re-draw everything onscreen. Contact me if you'd like a promo code to check out the rotation in either app (they rotate the same - QColor is more complex since it also draws intersections).
Enjoy,
Damien