iOS - Change storyboards upon rotation - Problems with Portrait - iphone

Ok so I have been testing out my own methods for changing the presented storyboards upon rotation and have been doing so with the -(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation method to be assigning self to another method which runs an if-else statement which checks the orientation and loads the correct storyboard appropriately. The problem I'm having is the checking if it's in portrait mode or the else part of the statement. When I run the app in simulator and rotate the device to landscape, it loads the landscape storyboard like it should. But when I rotate the device back to portrait mode, it doesn't change back to the portrait storyboard. At first I thought the problem was that I wasn't dismissing the view controllers but after doing that, it still didn't work. I realized that it was the else part of the statement that wasn't working. I stuck anNSLog(#"PMComplete") and still after running the app again and rotating to landscape and back, it still didn't show up in the log. Does anyone have any thoughts on what's wrong here? I am operating this on the viewcontroller for the portrait storyboard (assigned the view controller in the portrait storyboard the ViewController class). I also created this app without storyboards and created them from scratch and assigned the portrait one as the one that loads up from the AppDelegate.
Here is my ViewController.m file:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize isLandscape;
#synthesize isPortrait;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)viewChanged {
UIInterfaceOrientation theOrientation = self.interfaceOrientation;
UIStoryboard *portraitStoryboard;
UIStoryboard *landscapeStoryboard;
portraitStoryboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
landscapeStoryboard = [UIStoryboard storyboardWithName:#"LandscapeStoryboard" bundle:nil];
UIViewController *portraitViewController = [portraitStoryboard instantiateInitialViewController];
UIViewController *landscapeViewController = [landscapeStoryboard instantiateInitialViewController];
if (theOrientation == UIInterfaceOrientationLandscapeLeft ||
theOrientation == UIInterfaceOrientationLandscapeRight) {
[portraitViewController dismissViewControllerAnimated:NO completion:nil];
[self presentViewController:landscapeViewController animated:NO completion:nil];
NSLog(#"LSComplete");
}else{
[landscapeViewController dismissViewControllerAnimated:NO completion:nil];
[self presentViewController:portraitViewController animated:NO completion:nil];
NSLog(#"PMComplete");
}
}
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[self viewChanged];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end

Related

How to load a UIViewController in landscape mode if its parent is in Portrait mode?

I want to load a ViewController in landscape mode from a portrait parent. Parent is got fixed for portrait, it wont change its orientation, however the child view controller whichever is got pushed from the parent also loading portrait initially irrespective of the device orientation (if we rotate the device few more times then the orientation set properly for childs). So how to load child properly in initial time itself.
or
Is there any way I can set the orientation programatically, so that I can use it in ViewWillAppear method.
Thanx.
In your child UIViewController set these two methods:
- (BOOL)shouldAutorotate
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeLeft;
}
In parent UIViewController do:
[self.navigationController presentViewController:controller animated:YES completion:nil];
instead of:
[self.navigationController pushViewController:controller animated:YES];
If you want to present navigation bar in child:
DetailViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"DetailViewController"];
controller.title = #"Child";
You'll have to subclass UINavigationController with the two methods mentioned above instead in the child view controller.
MyNavigationController *nav = [[MyNavigationController alloc] initWithRootViewController:controller];
[self.navigationController presentViewController:nav animated:YES completion:nil];
Try this sample Project...........
https://www.dropbox.com/s/lrsz4dpeolpeu23/RotationDmeo.zip
If i got it right, you just add this methods to your child view controller
- (BOOL)shouldAutorotate {
return NO;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeLeft / Right;
}
To present your view use
[self presentViewController:viewController...]
And your view is presented in Landscape mode.
Use this in view did appear or will appear,
[[UIDevice currentDevice]performSelector:#selector(setOrientation:) withObject:(__bridge id)((void *)UIInterfaceOrientationLandscapeRight)];
Before that Enable all 4 orientations in your .plist
Add this on AppDelegate.m
- (NSUInteger)application:(UIApplication*)application
supportedInterfaceOrientationsForWindow:(UIWindow*)window
{
UIViewController *cont=self.vc;
if([cont isKindOfClass:[YourClass class]])
{
NSUInteger orientations = UIInterfaceOrientationMaskLandscapeRight;
NSLog(#"Landscape");
return orientations;
}
NSUInteger orientations = UIInterfaceOrientationMaskPortrait;
return orientations;
}

ViewController in UINavigationController orientation change

So I have the following hierarchy:
UINavigationController --> RootViewController (UIViewController) --> UITableViewController --> DetailViewController (UIViewController)
I want to lock the orientation on RootViewController to Portrait only, but leave all orientations for the rest view controllers.
If I put this to subclassed UINavigationController:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
-(NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
All view controllers are then locked to portrait.
My question is, is there a way to lock only RootViewController to Portrait, but leave all options for other view controllers?
check the link here for fixing autorotation in iOS 6 and set orientation support per view basis: http://www.disalvotech.com/blog/app-development/iphone/ios-6-rotation-solution/
Here is what you could do:
Create a custom navigation controller that is a subclass of UINavigationController, in your .m file:
- (BOOL)shouldAutorotate
{
return self.topViewController.shouldAutorotate;
}
- (NSUInteger)supportedInterfaceOrientations
{
return self.topViewController.supportedInterfaceOrientations;
}
In your AppDelegate.h,
#interface AppDelegate : UIResponder <UIApplicationDelegate> {
UINavigationController *navigationController;
ViewController *viewController;
}
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) ViewController *viewController;
and in AppDelegate.m,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// set initial view
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
viewController = [[ViewController alloc] initWithNibName:#"RootViewController" bundle:nil];
navigationController = [[CustomNavigationController alloc]
initWithRootViewController:viewController]; // iOS 6 autorotation fix
[navigationController setNavigationBarHidden:YES animated:NO];
self.window = [[UIWindow alloc]
initWithFrame:[[UIScreen mainScreen] bounds]];
[self.window setRootViewController:navigationController]; // iOS 6 autorotation fix
//[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
return YES;
}
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window // iOS 6 autorotation fix
{
return UIInterfaceOrientationMaskAll;
}
in your rootViewController, for whatever the event push the second view controller, do this:
- (IBAction)pushSecondViewController:(id)sender {
// push second view controller
SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
[self.navigationController pushViewController:secondViewController animated:YES];
}
in your each view controller, add
- (BOOL)shouldAutorotate{}
- (NSUInteger)supportedInterfaceOrientations{}
for iOS 6, you can set each view controller whatever the orientation support you want individually.
for iOS 5 and below, you can set
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{}
All the credits goes to John DiSalvo who wrote the sample app in the link.
Hope this helps.
in singleton
-(void)setOrientationSupport:(BOOL)flag{
flag_orientationSupport_ = flag;
}
-(BOOL)getOrientationSupport{
return flag_orientationSupport_;
}
in appdelegate
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if ([singleton getOrientationSupport])
return UIInterfaceOrientationMaskAll;
else
return UIInterfaceOrientationMaskPortrait;
}
add the following code in viewwillappear
for the viewcontroller you want support orientation
[singleton setOrientationSupport:YES];
to those controller you want disable orientation
[singleton setOrientationSupport:NO];
Put this in your Nav controller:
- (BOOL)shouldAutorotate
{
return self.topViewController.shouldAutorotate;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;
}
Now, add this to your root view controller:
- (BOOL)shouldAutorotate
{
return NO;
}
That should do it!
To delegate the responsibility of defining allowed orientations to subviews of a UINavigationController, one can use the visibleViewController property of the nav controller to make the navigation controller "ask" it's child views for their supported orientations. The following code should work (Swift) :
Sub-classed navigation controller:
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if let visibleView = self.visibleViewController {
return visibleView.supportedInterfaceOrientations()
} else {
return UIInterfaceOrientationMask.All
}
}
Sub-view (root view) of nav controller:
// Restrict to portrait
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.Portrait
}
I have been searching for the solution for hours!
So after implementing the needed methods everywhere. shouldAutorotate doesn't need to be set to YES because it is already set as default:
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
return UIInterfaceOrientationPortrait;
}
When it is time to show the UIViewController which needs the orientation different than the other views, I created a UIStoryboardSegue with this implementation inside:
#import "Showing.h"
#implementation Showing
- (void)perform{
NSLog(#"Showing");
UIViewController *sourceVC = self.sourceViewController;
UIViewController *presentingVC = self.destinationViewController;
[sourceVC.navigationController presentViewController:presentingVC
animated:YES
completion:nil];
}
#end
Inside the UIStoryboard I connected the views with this segue (showing):
It is just important, you are using
presentViewController:animated:completion:
AND NOT
pushViewController:animated:
otherwise the orientation won't be determined again.
I had been trying things like
[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait];
OR this one inside the UIViewController where the orientation should change, and I also tryied to call it inside my custom UIStoryboardSegues before presentingViewController and dismissViewController:
[UIViewController attemptRotationToDeviceOrientation];
OR
NSNumber *numPortrait = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
[[UIDevice currentDevice] setValue:numPortrait forKey:#"orientation"];
But no one of them worked. Of course the last example shouldn't be an option, because if apple will change anything of their api this could cause problems inside your app.
I also tried to use the AppDelegate method and always determine the orientation inside this method after looking for the correct UIInterfaceOrientation of the actual visibleViewController but then it sometimes happened to crash when switching from one to another orientation. So I'm still wondering why its made so complicated and there seems also not to be any documentation where it is explained correctly.
Even following this part didn't help me.

whose view is not in the window hierarchy! issue

My project was first created with a UITabBarController as the first view to appear, then I needed to add a custom splash screen that appears for 3 sec so I used a new UIViewController which appears before the UITabBarController and I set this custom splash screen as the first view to appear. However, after I did that change. At the moment my splash screen goes to the UITabBarController im receiving this error.
Warning: Attempt to present UITabBarController: 0x1cdcfe30 on SplashViewController: 0x1cdc55e0 whose view is not in the window hierarchy!
Im performing the change of view in my SplashViewController in this way:
#import "SplashViewController.h"
#interface SplashViewController ()
#end
#implementation SplashViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
timer = [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(changeView) userInfo:nil repeats:YES];
}
-(void)changeView{
[self performSegueWithIdentifier:#"splash" sender:self];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
By the way im using storyboards this is a strange error that appears in the console all the time after I added that splash screen and I cant figure out how to get rid of it.
Give Storyboard id "TabBarViewController" for TabBarController in Identity Inspector(Storyboard)
And implement changeView as-
-(void)changeView
{
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
TabBarViewController *tabBarViewController = [storyBoard instantiateViewControllerWithIdentifier:#"TabBarViewController"];
[[[[UIApplication sharedApplication] delegate] window] setRootViewController:tabBarViewController];
}
There's no need to add another controller for a splash screen. Just add a view (your splash screen view) on top of the view of the controller in the first tab. That view will appear at app start up, then after whatever delay you want, fade out that view and remove it from its superview.

Allow Rotation for QLPreviewController independent of parent View

my app doesn't support rotation. But I want to present a QLPreviewController that supports rotation.
I present the QLPreviewController like this:
[self presentModalViewController:thePrevController animated:NO];
How can I do this?
Enable all rotations in your application plist file. This will make all views rotate irrespective of the settings in the view controller.
Then subclass your root UINavigationController as below, adding the rotation control code for iOS5 and 6 depending on your requirements:
I was updating an old app with MainWindow.xib, so I changed the class of the navigation controller in the xib file to CustomNavigationController. But in a more modern app with say a main menu, you'd instantiate the nav controller like this:
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
MainMenuVC *masterViewController = [[MainMenuVC alloc] initWithNibName:#"MainMenuVC" bundle:nil];
self.navigationController = [[CustomNavigationController alloc] initWithRootViewController:masterViewController];
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
Subclass UINavigationController
#import <UIKit/UIKit.h>
#interface CustomNavigationController : UINavigationController
#end
#import "CustomNavigationController.h"
#interface CustomNavigationController ()
#end
#implementation CustomNavigationController
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
-(BOOL)shouldAutorotate
{
return NO;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
#end
Then subclass the QLPreview controller so you can override the rotation code which will enable rotation for the QLPreviewController only. The rest of the app with views pushed from your CustomNavContoller will not rotate as the CustomNavigationController is locked.
I added this interface and implementation at the top of the view controller where I wanted to present the QLPreviewController.
#interface RotatingQLPreviewController : QLPreviewController
#end
#implementation RotatingQLPreviewController
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
-(BOOL)shouldAutorotate
{
return YES;
}
#end
Then present your QLPreviewController using your subclass.
RotatingQLPreviewController *preview = [[RotatingQLPreviewController alloc] init];
preview.dataSource = self;
[self presentViewController:preview
animated:YES
completion:^(){
// do more stuff here
}];
This method should work for other modal views that you want to rotate, but I haven't tried it.
I implemented this method in the latest app I'm working on and works in both iOS5 and 6.
Hope it helps.

How to stop rotation of ABPersonViewController & ABNewPersonViewController in Landscape mode in iphone

I am using a ABPersonViewController and ABNewPersonViewController class by pushview controller.
ABPersonViewController *pvc = [[ABPersonViewController alloc] init];
[pvc setPersonViewDelegate:self];
[[self navigationController] pushViewController:pvc animated:YES];
In ABPersonViewController and ABNewPersonViewController page it is displaying in portrait mode. But when I rotate my iPhone then it is perfectly rotating in landscape mode. But I want to stop this rotation. If I rotate my iPhone in landscape mode it's view should be in portrait mode.
The solution for your problem is quite simple: just subclass UINavigationController like this:
#interface UINonRotatingNavigationController : UINavigationController {
}
#end
in your .h file, and in your .m file type:
#implementation UINonRotatingNavigationController {
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
Use it as your main navigation controller for the person picker - this should block it from rotating.
Hope this was helpful,
Paul