Only one view controller orientation issue when going back to pervious view controller in ios 7 - ios7

In my app we need only one view controller would be in all orientation mode other view controllers will be portrait mode only.
I am using below code and it's working perfectly but when coming back to pervious view controller it's not rotating in portrait mode when I am coming from Landscape mode.
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
// Get topmost/visible view controller
UIViewController *currentViewController = [self topViewController];
// Check whether it implements a dummy methods called canRotate
if ([currentViewController respondsToSelector:#selector(canRotate)]) {
// Unlock landscape view orientations for this view controller
return UIInterfaceOrientationMaskAllButUpsideDown;
}
// Only allow portrait (standard behaviour)
return UIInterfaceOrientationMaskPortrait;
}
- (UIViewController*)topViewController {
return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
if ([rootViewController isKindOfClass:[UITabBarController class]]) {
UITabBarController* tabBarController = (UITabBarController*)rootViewController;
return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
} else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController* navigationController = (UINavigationController*)rootViewController;
return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
} else if (rootViewController.presentedViewController) {
UIViewController* presentedViewController = rootViewController.presentedViewController;
return [self topViewControllerWithRootViewController:presentedViewController];
} else {
return rootViewController;
}
}

I've been working against the same issue. The orientation can be forced back to its normal configuration by adding an adjustment in viewWillAppear of the controller you are going back to:
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
[[UIDevice currentDevice] setValue:value forKey:#"orientation"];
}

Related

How to rotate a video embed in UIWebView (for iOS 7 only)?

The app I'm working on is portrait oriented, but when a video is running (it is embed in a webview), I need to re-orient the video in landscape mode. How should I do that? I found a solution, which worked just fine until days ago :). I believe it's because iOS 7 updates, but I'm not sure. So, this is what I previously used, but it is not working anymore because window and class name are always nil.
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
id presentedViewController = [window.rootViewController presentedViewController];
NSString *className = presentedViewController ? NSStringFromClass([presentedViewController class]) : nil;
if (window && [className isEqualToString:#"MPInlineVideoFullscreenViewController"]) {
return UIInterfaceOrientationMaskAll;
} else {
return UIInterfaceOrientationMaskPortrait;
}
I found a solution by myself, finally! I implemented the following method in AppDelegate and it worked. My problem was that, at first, I didn't check the right view controller.
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
NSString *className = NSStringFromClass([window class]);
if ([((UINavigationController *)window.rootViewController) respondsToSelector:#selector(visibleViewController)]) {
className = NSStringFromClass([((UINavigationController *)window.rootViewController).visibleViewController class]);
}
if ([className isEqualToString:#"MPFullscreenWindow"] || [className isEqualToString:#"MPInlineVideoFullscreenViewController"]) {
return UIInterfaceOrientationMaskAll;
} else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
return UIInterfaceOrientationMaskLandscape;
} else {
return UIInterfaceOrientationMaskPortrait;
}
Here's a solution that will allow rotation on any additional windows presented in the iPhone app (such as a vide player) but remain landscape in an iPad app. Place it in your app delegate.
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
return UIInterfaceOrientationMaskLandscape;
} else {
if (window == self.window
|| ![window isMemberOfClass:[UIWindow class]]) {
return UIInterfaceOrientationMaskPortrait;
}
if ([window isEqual:[[UIApplication sharedApplication] windows][1]]) {
// Rotate the secondary window.
return UIInterfaceOrientationMaskAllButUpsideDown;
}
return UIInterfaceOrientationMaskPortrait;
}
So I figured I'd share what my solution was. Actually branching off of the OP question, just modified it. Works for me on iOS 7 and 8.
My approach was different as I have a toggle switch in another view controller that enables portrait or landscape.
Anyways, here it is.
UPDATED:
Ok so the previous method broke the launch screen. Example: If you have your device in landscape, and launched but it's a portrait app, it will rotate, BUT, the window will get cut in half. Pain in the butt esp if you have some nice loading screens going on. Anyways, the replaced code below fixes that AND allows video rotation. Obviously not everybody will be using a rotation switch, just adjust accordingly.
//Allow video only rotation in portrait mode.
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
//Switch for Rotation
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
BOOL switchOn = [userDefaults boolForKey:#"Rotation"];
if (switchOn) {
window.autoresizingMask=(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth);
[window setFrame:[[UIScreen mainScreen] bounds]]; //Add
return UIInterfaceOrientationMaskAllButUpsideDown;
}
else {
id presentedViewController = [window.rootViewController presentedViewController];
NSString *className = presentedViewController ? NSStringFromClass([presentedViewController class]) : nil;
if ((window && [className isEqualToString:#"MPInlineVideoFullscreenViewController"]) ||
[className isEqualToString:#"MPMoviePlayerViewController"] ||
[className isEqualToString:#"AVFullScreenViewController"]) {
window.autoresizingMask=(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth);
[window setFrame:[[UIScreen mainScreen] bounds]]; //Add
[window makeKeyAndVisible];
return UIInterfaceOrientationMaskAllButUpsideDown;
}
window.autoresizingMask=(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth);
[window setFrame:[[UIScreen mainScreen] bounds]]; //Add
return UIInterfaceOrientationMaskPortrait;
}
self.window.autoresizingMask=(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth);
[self.window setFrame:[[UIScreen mainScreen] bounds]]; //Add
[self.window makeKeyAndVisible];
}
UPDATED AGAIN:
So the previous edit I laid out had a weird flicker of the view below the movie player. This seems to have fixed that. Tested on 6+ device, and iOS 7/8 in the simulator.
Hope this helps somebody.
Swift 3, I sorted out on this way (your info.plist / project settings can have ONYL the portrait orientation checked) :
// MARK: - Orientation
extension AppDelegate {
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
// Let webView video to be rotated
if window != self.window {
return window?.rootViewController?.supportedInterfaceOrientations ?? .all;
}
// All other screens are portrait
return .portrait;
}
}
Try It....
-(BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
If you don't want your UIViewController to be able to rotate when the video isn't on the screen. Use this--
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if(webView && webView.superView) return YES;
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
This is very similar to how I solved for iOS 7, but this doesn't work for iOS 8. MPFullscreenWindow is no longer returned and Xcode complains about breaking constraints.
My solution seems to be fairly general, and takes care of some weird behavior on iOS 7 where window sometimes passed in as nil.
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
return UIInterfaceOrientationMaskAll;
}
else
{
// when dismissing a view controller, the view controller being returned to isn't in control of the orientiation
// (shouldAutorotate and supportedInterfaceOrientations seem to be called, but they aren't observed then,
// rather only when the device is rotated after that view is fully showing)
// instead, the result of this method only is what's observed
// we could return different values depending on which view controller is frontmost, but currently it seems
// good enough to call supportedInterfaceOrientations of the frontmost view controller and return that
// on ios7, critical calls to this method are often passed window=nil, in that case use self.window instead
UIViewController *frontViewController = window ? window.rootViewController : self.window.rootViewController;
// special case only when transitioning to or from a presented view controller
if (frontViewController.presentedViewController &&
(frontViewController.presentedViewController.isBeingDismissed || frontViewController.presentedViewController.isBeingPresented))
{
if (frontViewController.presentedViewController && !frontViewController.presentedViewController.isBeingDismissed) {
frontViewController = frontViewController.presentedViewController;
}
if ([frontViewController isKindOfClass:[UINavigationController class]]) {
frontViewController = ((UINavigationController *)frontViewController).topViewController;
}
// return whatever the front view controller's supportedInterfaceOrientations returns, since it normally is ignored for some reason
return [frontViewController supportedInterfaceOrientations];
}
else
{
// return this normally, this gets intersected with the result of the front view controller's supportedInterfaceOrientations
return UIInterfaceOrientationMaskAllButUpsideDown;
}
}
}
=> Put below method inside your AppDelegate class, it allow to play video in landscape mode even when device orientation is locked to portrait mode only:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)windowx
{
if ([[self.window.rootViewController presentedViewController] isKindOfClass:[MPMoviePlayerViewController class]] ||
[[self.window.rootViewController presentedViewController] isKindOfClass:NSClassFromString(#"MPInlineVideoFullscreenViewController")])
{
if ([self.window.rootViewController presentedViewController].isBeingDismissed)
{
return UIInterfaceOrientationMaskPortrait;
}
else
{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
}
else
{
return UIInterfaceOrientationMaskPortrait;
}
}

How do I restrict orientation per view controller in iOS7 in a navigation controller hierarchy

My app is a UITabBarController --> UINavigationController --> UITableViewController --> UIViewController.
I want to do 2 things:
Prevent my tableview from rotating, I want it to always stay portrait.
FORCE & Allow my UIViewcontroller to rotate landscapeleft.
What I know:
I understand that the viewcontroller at the top of the hierarchy controls rotation. This would be my UITabBarController? Or rather its only viewcontroller which would be at objectIndex:0?
My project settings allow for Portrait, LL and LR rotations. Im thinking this is the pattern I need to follow in order to solve this is allow ALL at the top level to rotate and then control each vc individually, correct?
This is what I have found so far in SO.
So for my top hierarchy, i set the project settings to allow rotation to Portrait, LL and LR.
and then in my tableviewcontroller which i dont want to rotate:
-(BOOL)shouldAutorotate{
return NO;
}
-(NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
and finally in my uiviewcontroller which I want to rotate:
-(NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
-(BOOL)shouldAutorotate{
return YES;
}
However this does not work. I can rotate both in any direction. I also dont know how to force rotation LL when I get to my uivc which is called from a modal segue from my tablevc.
Any help understanding this mess would be greatly appreciated :)
Simple but it work very fine. IOS 7.1 and 8
AppDelegate.h
#property () BOOL restrictRotation;
AppDelegate.m
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if(self.restrictRotation)
return UIInterfaceOrientationMaskPortrait;
else
return UIInterfaceOrientationMaskAll;
}
ViewController
-(void) restrictRotation:(BOOL) restriction
{
AppDelegate* appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
appDelegate.restrictRotation = restriction;
}
viewDidLoad
[self restrictRotation:YES]; or NO
Ok, here it is. kinda complicated.
Project Settings must allow P, LL & LR
Storyboard is a UINavController with a UITableViewController with a push bar button segue to a UIViewController.
All scenes in storyboard must have inferred as orientation in simulated metrics. Just saying, cause after a while i had them all with different settings after testing so much.
Must have a Class for NavController, TableViewController and the given UIVController. My app started out as Single view, then I dragged in a UITVC and finally embedded the UITVC in a UINVC. Then I connected the UIVC to the UITVC bar button item I dragged in via push segue.
Set apps window.rootvc to the navVC, your top hierarchy vc (don't forget to set that ID in storyboards or it'll crash of course):
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
UINavigationController *myNavC = (UINavigationController*)[mainStoryboard instantiateViewControllerWithIdentifier:#"MainNav"];
self.window.rootViewController = myNavC;
Tell big boss UINVC everyone can rotate:
- (BOOL)shouldAutorotate {
return self.topViewController.shouldAutorotate;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return self.topViewController.supportedInterfaceOrientations;
}
Restrict tablevc so it won't rotate:
- (BOOL)shouldAutorotate { return NO; }
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return (UIInterfaceOrientationMaskPortrait);
}
Allow last UIVC to rotate:
- (BOOL)shouldAutorotate { return YES; }
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return (UIInterfaceOrientationMaskAllButUpsideDown);
}
Responce
It should not be confused UIDeviceOrientation and interface orientation here is my solution
Appdelegate.h
#property () UIInterfaceOrientationMask restrictRotation;
AppDelegate.m
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
UIInterfaceOrientationMask restrictionOrientation = 0;
if (_restrictRotation == 0)
return UIInterfaceOrientationMaskAll;
UIDeviceOrientation currentOrientation = [[UIDevice currentDevice] orientation];
switch (currentOrientation)
{
case UIDeviceOrientationPortrait:
if (!(UIInterfaceOrientationMaskPortrait & _restrictRotation))
restrictionOrientation = UIInterfaceOrientationMaskAll;
else
restrictionOrientation = UIInterfaceOrientationMaskPortrait;
break;
case UIDeviceOrientationPortraitUpsideDown:
if (!(UIInterfaceOrientationMaskPortraitUpsideDown & _restrictRotation))
restrictionOrientation = UIInterfaceOrientationMaskAll;
else
restrictionOrientation = UIInterfaceOrientationMaskPortrait;
break;
case UIDeviceOrientationLandscapeLeft:
if (!(UIInterfaceOrientationMaskLandscapeLeft & _restrictRotation))
restrictionOrientation = UIInterfaceOrientationMaskAll;
else
restrictionOrientation = UIInterfaceOrientationMaskPortrait;
break;
case UIDeviceOrientationLandscapeRight:
if (!(UIInterfaceOrientationMaskLandscapeRight & _restrictRotation))
restrictionOrientation = UIInterfaceOrientationMaskAll;
else
restrictionOrientation = UIInterfaceOrientationMaskPortrait;
break;
case UIDeviceOrientationUnknown:
if (!(UIInterfaceOrientationUnknown & _restrictRotation))
restrictionOrientation = UIInterfaceOrientationMaskAll;
else
restrictionOrientation = UIInterfaceOrientationMaskPortrait;
break;
default:
NSLog(#"Unknown orientation");
break;
}
return restrictionOrientation;
}
In each vc add the function
-(void) restrictRotation:(UIInterfaceOrientationMask) restriction
{
AppDelegate* appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
appDelegate.restrictRotation = restriction;
}
viewDidAppear
//
// Set vc orientation
//
[self restrictRotation:UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight];
or what you want.
[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger: UIInterfaceOrientationPortrait] forKey:#"orientation"];
or what you want.

iOS6 landscape app with only one controller multioriented popViewController issue

I have this setup to support only landscape orientation in most of my viewcontrollers
My app delegate has this piece of code:
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
NSUInteger orientations = UIInterfaceOrientationMaskLandscape;
if (self.window.rootViewController) {
UIViewController * pressented = [[((UINavigationController *)self.window.rootViewController) viewControllers] lastObject];
orientations =[pressented supportedInterfaceOrientations];
}
return orientations;
}
And in most viewcontrollers this:
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
My problem comes when I push THIS controller (the one I would like to rotate):
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskALL;
}
It rotates perfectly BUT when I pop the viewcontroller (tap the back button of the navigation bar) with the orientation in portrait, the presenting viewcontroller also sets it's orientation to Portrait.
How can I make the presenting viewcontroller stays locked on landscape, or force the problematic controller to rotate back to landscape before popping.
Add this to your portrait view controller:
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
UIViewController* dummyController = [[UIViewController alloc] init];
[self presentViewController:dummyController animated:NO completion:^{
[self dismissViewControllerAnimated:NO completion:nil];
}];
}
I know it's a hack, but it works. Anyone knows a better solution?

How to load UIView in Portrait mode irrespective of device orientation - IOS 6

I am working on iOS 6 application, I have handled the UIview rotation using following methods...
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (BOOL) shouldAutorotate {
return YES;
}
Everything is working fine, problem is initial view loading is decided based on device orientation, for e.g.: if I keep my device in landscape mode even though I have returned as forcibly portrait in supportedInterfaceOrientations view is showing in landscape only, once device is rotated to portrait after that it is not going to landscape mode, all working fine. Is it possible to load a View at certain mode irrespective of the device orientation?
I have googled and nothing worked.
NOTE: I am using navigation controller. And I have added category for UINavigationController.
// Custom categoy to handle orientation in IOS6
#implementation UINavigationController (Rotation_IOS6)
-(BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
#end
thanks
Use this method:
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationMaskPortrait;
}
Add this method along with category for UINavigationController in appDelegate.
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
//check for which controller u need these methods
if([navigationController.visibleViewController isKindOfClass:[yourViewController class]]) //provide specific view controller where u want protrait
{
[navigationController shouldAutorotate];
[navigationController supportedInterfaceOrientations];
[navigationController preferredInterfaceOrientationForPresentation];
}
return UIInterfaceOrientationMaskAll;
}
Note : remove from all viewController if u have added category for UINavigationController. It should be only in appDelegate.

viewWillAppear not called in BCTabBarController

I've large project where customer want's to customize tabbar. I've choose BCTabBarController to replace UITabbarController. After few fixes it works fine but after testing I found one bug:
ViewWillAppear, ViewDidAppear, ViewWillDisappear ViewDidDisappear methods not called in selectded view controller and not called into BCTabBarController.
This problem appears after BCTabBarController show modal controller from instance of BCTabBarController class.
I've posted issue to github repo of briancolins, but still have no answer.
Here some code where I calling present modal view controller:
- (void) presentProperlyModalViewController:(UIViewController *)modalViewController animated:(BOOL)animated
{
if ([[self controllerToPresentModalFrom] respondsToSelector:#selector(presentViewController:animated:completion:)]) // For iOS 5
{
[[self controllerToPresentModalFrom] presentViewController:modalViewController animated:animated completion:^(){}];
}
else
{
[[self controllerToPresentModalFrom] presentModalViewController:modalViewController animated:animated];
}
}
-(void) dismissProperlyModalViewControllerAnimated:(BOOL)animated
{
if ([self respondsToSelector:#selector(dismissViewControllerAnimated:completion:)]) {
[self dismissViewControllerAnimated:animated completion:^(){}];
}
else
{
[self dismissModalViewControllerAnimated:YES];
}
}
UPDATE: this issue not reproduced in iOS5 but present at iOS 4.3
As you indicated. iOS 5 forwards the messages, where previous versions do not. Here's how I handle a similar situation:
- (BOOL)needsMessageForwarding:(UIViewController *)vc {
if ( [vc isKindOfClass:[UINavigationController class]] == NO)
return YES;
NSString *ver = [UIDevice currentDevice].systemVersion;
if ( [ver characterAtIndex:0 < '5'] )
return YES;
return NO;
}
- (void) viewWillAppear:(BOOL)animated {
...
if ( [self needsMessageForwarding:modalViewController] )
[modalViewController viewWillAppear:animated];
...
}
// repeat pattern in the other viewWill... viewDid... functions.
In my situation I have a list of view controllers that could be visible, so I manage which view controller is visible and forward the message to it.