Single UIViewController Auto-rotation inside UITabBar - ios

Hi my TabBarViewController Hierarchy is like this. All ViewControllers attached with tabbar are without navigationController.
UIViewController (Home View), when Pushed it navigate to tabBar based ViewController at index 0, User Can come back from tabBarViewControllers to home viewController at any time using back button in navigation bar.
UITabBarViewController (BaseViewController)
-ViewController0,(NO Navigation ViewController)
-ViewController1 (NO Navigation ViewController)
-ViewController2 (NO Navigation ViewController)
-ViewController3 (NO Navigation ViewController)
-ViewController4 (NO Navigation ViewController)
I used this Approach of Tabbar based ViewController, because Tabbar is not Home ViewController.
I want to auto rotate only ViewController2 in Portrait and Landscape. My Project is only in Portrait Mode.
I tried many thing like THIS, But it not getting.

Hi After lot research What i have found, whether it is Tabbar or UIVicontroller.
As per my Question, My project is in Portrait Mode and I want only single view Controller Auto rotation.Below are the steps, which helped me.
1 - In App Delegate.h
#property (assign, nonatomic) BOOL shouldRotate;
2 - In App Delegate.m
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED
{
_shouldRotate = [[NSUserDefaults standardUserDefaults]boolForKey:#"rotateKey"];
NSLog(#"Did I get to InterfaceOrientation \n And the Bool is %d",_shouldRotate);
if (self.shouldRotate == YES){
return UIInterfaceOrientationMaskAll;
}else{
return UIInterfaceOrientationMaskPortrait;
}
}
3 - Now Which UIViewController, You want Auto rotation,
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:YES];
BOOL rotate = YES;
[[NSUserDefaults standardUserDefaults]setBool:rotate forKey:#"rotateKey"];
[[NSUserDefaults standardUserDefaults]synchronize];
}
-(BOOL)shouldAutorotate
{
return YES;
}
4 -Trick Part is
If you do all above steps, your view controller will get auto rotated, if u come back from current view controller, which is landscape mode. the previous view controller will get auto rotate in landscape and it will keep chain.
So, to avoid this,
If you are going from view Controller A to View Controller B. View Controller is auto- rotation, then in View Controller A-
5 - Use this code -
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:YES];
BOOL rotate = NO;
[[NSUserDefaults standardUserDefaults]setBool:rotate forKey:#"rotateKey"];
[[NSUserDefaults standardUserDefaults]synchronize];
}
-(BOOL)shouldAutorotate
{
return NO;
}
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)toInterfaceOrientation
{
return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 90000
- (NSUInteger)supportedInterfaceOrientations
#else
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
#endif
{
dispatch_async(dispatch_get_main_queue(), ^{
// UI Updates
});
return UIInterfaceOrientationMaskPortrait;
}

Related

How to present a view controller in portrait or landscape, in application that is portrait-only

I have an iPhone application most of which is portrait-only, but with one view controller (a video player screen) that has to support both portrait and landscape (this is for iOS 8 only). To achieve this, I have set the app's plist to support portrait and both kinds of landscape, then subclassed UINavigationController; in this subclass I override
- (BOOL)shouldAutorotate {
return YES;
}
and
- (NSUInteger)supportedInterfaceOrientations {
if ([self.visibleViewController isKindOfClass:[MyVideoPlayerScreen class]]) {
return UIInterfaceOrientationMaskAllButUpsideDown;
} else {
return UIInterfaceOrientationMaskPortrait;
}
}
This mostly works as expected: the initial screens are all portrait-only, and remain in portrait even when the device is turned to landscape. The video player, when initially presented:
MyVideoPlayerScreen *newVC = [MyVideoPlayerScreen new];
[self.navigationController pushViewController:newVC animated:YES];
is also in portrait but will then rotate to landscape when the device is turned - all good so far.
The problem is that if I turn the device to landscape, the video player goes landscape as well, but then when I dismiss the video player screen via the back button, the underlying view controller (which is supposed to be portrait-only) is now also in landscape. If I rotate the device back to portrait the view controller rotates back to portrait as well, and it is then correctly locked in portrait-only from that point on.
How can I get the original view controller (which is supposed to be portrait-only) to automatically go back to portrait when the landscape view controller above it is popped?
This question has been asked a million times, but it seems that the fixes that were posted for it are all hacks that don't work in iOS 8 any more.
Update: I have found a "sort-of" fix for this that does work in iOS 8. In my UINavigationController subclass, I handle the <UINavigationControllerDelegate> protocol. I implemented this method:
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
if (![viewController isKindOfClass:[MyVideoPlayerScreen class]]) {
// if the current orientation is not already portrait, we need this hack in order to set the root back to portrait
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (orientation != UIInterfaceOrientationPortrait) {
// HACK: setting the root view controller to nil and back again "resets" the navigation bar to the correct orientation
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UIViewController *vc = window.rootViewController;
window.rootViewController = nil;
window.rootViewController = vc;
}
}
}
This at least leaves the UI in the state I want it to be in. The problem is that when the top-level view controller is dismissed and animated off-screen, the underlying view controller is still in landscape; then it suddenly jumps to portrait. Not ideal, but better than nothing.
Update 2: I should have added that I am pushing the second view controller like so:
ViewControllerPortraitLandscape *newVC = [ViewControllerPortraitLandscape new];
[self.navigationController pushViewController:newVC animated:YES];
Update 3: OK, this is totally doable, in a way that works for iOS 6 and up. The fix even kind of makes sense, although the reason for it's working does not seem to be in the documentation anywhere. Basically, in the view controller that you need to be reset to portrait when the top-level view controller is dismissed while the device is still in landscape, you just need to add this:
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
Although my subclass of UINavigationController is entirely responsible for the rotation calls, breakpoints show that supportedInterfaceOrientations is called on the underlying view controller just before the top-level is dismissed, and it's called on the navigation controller after the top-level is dismissed. So I'm inferring that this call to the view controller itself is made by iOS in order to determine what orientation the underlying view controller should be in (and it does not ask the nav controller for this); if it's not explicitly overridden it will return the all-but-upside-down parameter, so iOS just leaves it where it is, in landscape.
Turns out this is an easy fix: you can drive the entire process via a subclass of UINavigationController as I posted here (i.e. not implementing any of these rotation methods in the view controllers themselves), except that for any view controller that needs to be portrait-only, you also need to implement this:
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
With breakpoints, you can see that this method is called before the pushed view controller above it is dismissed; iOS presumably uses this call to determine what orientation the "revealed" view controller should be in (the same call to the navigation controller subclass is called after the top-level view controller is dismissed).
With this method implemented in the view controller, everything works as expected.
Be portrait EXCEPT when presenting a particular UIViewController subclass. This article helped a lot: ~~removed because of spam~~
set the Info.plist to support, portrait, landscape left, landscape right
implement application:supportedInterfaceOrientationsForWindow: like so:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;
}
// (I specifically want landscape left for the movie viewing)
subclass UINavigationController(the windows' rootViewController) and override like so:
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait; // enforces the “portrait everything” requirement
}
finally, I had to make sure the custom player view controller would "override" the supported orientation:
- (BOOL)shouldAutorotate
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscapeLeft;
}
Result:
UIViewController subclass presents but when dismissing the custom view controller, the presenting view controller is Portrait.
From fantageek article: "The system intersects the view controller’s supported orientations with the app’s supported orientations (as determined by the Info.plist file or the app delegate’s application:supportedInterfaceOrientationsForWindow: method) to determine whether to rotate."
I think you should change to this and try again
-(NSUInteger)supportedInterfaceOrientations {
if ([self.visibleViewController isKindOfClass:[MyVideoPlayerScreen class]])
{
return UIInterfaceOrientationMaskLandscape;
}
else
{
return UIInterfaceOrientationMaskPortrait;
}
}
Here's how you do it.
Implement these 3 methods on both presenting and presented controller:
- (BOOL)shouldAutorotate {
return NO; //-- for presented controller use YES
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape; //-- any orientation you need
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeRight;
}
and now you can use in the presenting controller:
[self presentViewController:presentedController animated:true completion:nil];
This way, when you go back to presenting controller, it will have the correct orientation.
My case has 3 view controller:
- first view controller: portrait
- second view controller: landscape right (has navigation controller and was presented by first view controller)
- third view controller: portrait (has navigation controller and was pushed by second view controller )
And my solution had already here. Hope this helps

navigation between viewcontrollers when orientation changes

I am developing ios application that uses two viewcontrollers A and B. A supports both orientations landscape and portrait, when B is only in portrait mode. my goal is B viewcontroller always to be only in portrait mode when I navigate from A to B .I make restrictions on B viewcontroller in the navigationcontroller.
- (BOOL)shouldAutorotate
{
_navigation = (MBNavigationController *) self.frontViewController;
id currentViewController = [_navigation visibleViewController];
if ([currentViewController isKindOfClass:[MBViewController class]])
return NO;
return YES;
}
when A is in portrait orientation and I navigate to B everything works fine. but when A is in landscape mode and I do the same, part of the B controller view is out scenes. please help me to find the right solution.
One possible solution is present the B viewController with another navigationController in which
// sysversion>=ios6
- (BOOL)shouldAutorotate
{
return NO;
}
// sysversion<=ios5
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}

Force Landscape ViewController in iOS 7

I have more than ten ViewControllers in portrait mode, but I need to force a single one in Landscape mode regardless of the device´s orientation.
Here is the solution:
1) Embed the LandscapeViewController in a subclassed NavigationController and connect it from your PortraitViewController using a modal segue.
2) Subclass UINavigationController
LandscapeNavigationController.m
-(BOOL)shouldAutorotate
{
return NO;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeLeft;
}
3) Don't forget to dismiss your modal VC (in this case from my Bar Buttom Item Action)
- (IBAction)goBack:(id)sender
{
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}

Maintaining orientation of parent view after modal view has been presented with different orientation

So I am developing an iPad app that supports only landscape mode except for on one modal view controller. The issue I am having is that once I present the modal view and change the orientation to portrait then dismiss the view, the parent view (which should only support landscape) is in portrait mode until I rotate the device in which it then goes back to landscape and stays that way. I have been beating myself up trying to figure out how to keep the parents view original orientation but haven't been able to find a solution.
I have the following code in my app delegate to allow orientation changes on only that single modal view (GalleryPhotoViewer) :
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
NSUInteger orientations = UIInterfaceOrientationMaskAllButUpsideDown;
if(self.window.rootViewController){
UIViewController *presentedViewController = [[(UINavigationController *)self.window.rootViewController viewControllers] lastObject];
//Support Portrait mode only on Photoviewer
if ([[presentedViewController presentedViewController] isKindOfClass:GalleryPhotoViewController.class] ) {
orientations = UIInterfaceOrientationMaskAll;
}else{
orientations = [presentedViewController supportedInterfaceOrientations];
}
}
return orientations;
}
From the parent class (PhotosViewController) I am calling :
GalleryPhotoViewController *gpView = [GalleryPhotoViewController new];
[self presentViewController:gpView animated:YES completion:nil];
Also in my parent (and other views) I have the following code to disallow portrait mode :
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if(interfaceOrientation == UIInterfaceOrientationPortrait) {
return YES;
} else {
return NO;
}
}
Any ideas on how I can keep the orientation on my parent view? I was thinking about possibly just programmatically changing the orientation on the parent in the viewWillAppear method once the modal is dismissed but then I wouldn't know what the previous orientation was, not to mention I haven't been able to find code to do this regardless for ios6.
EDIT/SOLUTION : So I found a solution and what I ended up doing was leaving the application:supportedInterfaceOrientationsForWindow: code and just adding the UINavigation subclass to the parent view that was presenting the modal view and everything worked as expected and the parent retained its original orientation while the modal was able to change freely.
In my parent :
//To make sure that this view remains in Landscape
#implementation UINavigationController (Rotation_IOS6)
-(BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
#end
Thanks #matt for the suggestions.
I think the problem is your use of application:supportedInterfaceOrientationsForWindow:. Instead, get rid of that, and start with a UINavigationController subclass and make that the class of the root view controller that is your navigation interface. Then:
In the UINavigationController subclass, return UIInterfaceOrientationMaskLandscape from supportedInterfaceOrientations.
In the presented (modal) view controller, return UIInterfaceOrientationMaskAll from supportedInterfaceOrientations.

Prevent rotation of ABPersonViewController and ABNewPersonViewController on iOS6

In direct reference to this question:
How to stop rotation of ABPersonViewController & ABNewPersonViewController in Landscape mode in iphone
How can I prevent this screen from rotating in iOS 6 when I am not the one pushing the view controller? Scenario being I create a new contact, and the user then presses either the 'Create new contact' or 'Add to existing contact' buttons. The screen produced is the ABNewPersonViewController but because I do not have direct access to the rotation methods, I am unable to prevent it from rotating.
Screenshot:
The above image is taken from a subclass of the ABUnknownPersonViewController, in this subclass the only functionality I have implemented is to override the rotation methods as follows:
- (BOOL)shouldAutorotate
{
return NO;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
if(toInterfaceOrientation == UIInterfaceOrientationPortrait)
return YES;
return NO;
}
However, the issue is, I cannot then subclass the ABNewPersonViewController screen that is pushed when one of the buttons in the above image is pressed to override the rotation on iOS 6. Any ideas on how I can legitimately get access to these buttons OR override the rotation on the screen that is pushed to prevent it from doing so?
UPDATE 1:
I attempted to create a category on the ABNewPersonViewController and ABUnknownPersonViewController that overrode the rotation methods (not ideal, I know) and then globally imported, but this did not work. Other than this, I am completely stuck for ideas on how to override this behaviour. Any suggestions?
UPDATE 2:
Is it possible to gain a reference to the buttons in that UITableView and override the methods they call? Or is this in violation of Apple terms by accessing private APIs? Trying to investigate this approach so far and not really getting anywhere.
On iOS 6, rotation handling has changed. There are two options to prevent the rotation:
You can set your whole app to only support portrait orientation in your Info.plist.
You can override a method in your application delegate:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return UIInterfaceOrientationMaskPortrait;
}
As that method is called on your delegate whenever the orientation changes or a new view controller is pushed, you can even use it to temporarily disable landscape display:
// In AppDelegate.h:
#property (nonatomic) BOOL portraitOnly;
// In AppDelegate.m:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return _portraitOnly ? UIInterfaceOrientationMaskPortrait : UIInterfaceOrientationMaskAllButUpsideDown;
}
// to switch to portrait only:
((AppDelegate *)[UIApplication sharedApplication].delegate).portraitOnly = YES;
// to switch back to allowing landscape orientations as well:
((AppDelegate *)[UIApplication sharedApplication].delegate).portraitOnly = NO;
Both methods are perfectly acceptable for App Store submission, as these only use published and documented behavior.
Going off of Tammo Freese answer. I assume the view that needs to be allow landscape is lower in the view stack than your custom ABUnknownPersonViewController. If so in your ABUnknownPersonViewController subclass over add this
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
((AppDelegate *)[UIApplication sharedApplication].delegate).portraitOnly = YES;
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:(BOOL)animated];
((AppDelegate *)[UIApplication sharedApplication].delegate).portraitOnly = NO;
}
In iOS 6 what orientations view controllers are allowed to rotate to is controlled by the top-most full screen view controller, and autorotation methods are no longer called down the view controller hierarchy.
Instead of creating a subclass of the ABPersonViewController, create a subclass of UINavigationController with this code:
- (BOOL)shouldAutorotate
{
return NO;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
Use this new subclass when creating the UINavigationController for the ABPersonViewController you are presenting. If the UINavigationController is not being presented modally (and is instead nested in a UITabBarController or another view controller), you will need to also put these methods in that view controller as well.

Resources