1. The VC direction inside the navigation controller stack is determined by the navigation controller. Nav — A — B — C, the rotation method of C doesn’t work, it depends on nav-(BOOL)shouldAutorotateand-(UIInterfaceOrientationMask)supportedInterfaceOrientations.

The solution is to rewrite the nav rotation method and point to the topViewController:

-(BOOL)shouldAutorotate{
   return self.topViewController.shouldAutorotate;
}

-(UIInterfaceOrientationMask)supportedInterfaceOrientations{
    return self.topViewController.supportedInterfaceOrientations;
}
Copy the code

In the case of UITabBarController, we’re passing along the result of its selectedViewController.

2. Rotation logic flow is: the direction of the phone changes –> notify APP –> call the rotation method of the key VC(TabBar or Nav) inside APP –> get rotatable and support the current device direction –> rotate to the specified direction.

The initial logical flow is physically oriented. So if A pushes to B, A only supports portrait screen, while B only supports landscape screen. If the physical direction of the phone does not change at this time, THEN B will still support portrait screen as A, even if it only supports landscape screen and problem 1 is solved.

Solution: Force rotation.

@implementation UIDevice (changeOrientation)

+ (void)changeInterfaceOrientationTo:(UIInterfaceOrientation)orientation
{
   if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
       SEL selector             = NSSelectorFromString(@"setOrientation:");
       NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
       [invocation setSelector:selector];
       [invocation setTarget:[UIDevice currentDevice]];
       int val                  = orientation;
       [invocation setArgument:&val atIndex:2];
       [invocation invoke];
   }
}


@end
Copy the code

Give UIDevice a category and implement it by calling setOrientation:, a private method.

3. After problems 1 and 2 are solved, the basic scheme of the whole project is determined. A typical project will have a main orientation, and most screens will have that orientation, like portrait, and then have a specific orientation for a specific interface.

So the solution is:

  • Configure support for all possible directions in target –> General –> Development Info
  • With baseViewController, all project VCS inherit from it and write default orientation Settings in baseVC, which is the orientation supported by most interfaces.
  • And then on the special directions screen, rewrite-(BOOL)shouldAutorotateand-(UIInterfaceOrientationMask)supportedInterfaceOrientationsTo get what he wants.
  • Special interface because forced rotation, so in the interface is rotated to the desired direction:
  -(void)viewWillAppear:(BOOL)animated{
   [UIDevice changeInterfaceOrientationTo:(UIInterfaceOrientationLandscapeLeft)];
}
Copy the code
4. Test results of push and POP:

To verify that the solution in Question 3 meets the requirements, satisfying the requirements means that each page can display the directions it supports without interfering with other interfaces. So test push and pop.

The test variables are:

  • Is the current interface default or special, here only portrait is set as the default, landscape is special. The default means that the VC simply inherits baseVC’s direction-dependent methods and does nothing extra.
  • Is the interface push or pop
  • Is the next screen a default or a special case?
  • Is the next screen shouldAutorate YES?

Before and after the two interface direction of the same painting, the result is certainly good, do not test. The final test results are as follows:

action The current The target Target rotatable The results of
push The default special ✔ ️ successful
push The default special failure
push special The default ✔ ️ Failed (2)
push special The default (3) failure
pop The default special ✔ ️ successful
pop The default special failure
pop special The default ✔ ️ (1)
pop special The default (1)
  • Success represents the rotation of the target interface in the desired direction
  • Tag 1: Default style for direct display without rotation (portrait).
  • Tag 2: No orientation switch from landscape to portrait, why? Because the direction of the UIDevice has not been changed, the switching effect is not triggered. So forced rotation is also called when the special interface leaves. In fact, as long as the neighboring directions are different, the forced rotation should be triggered when switching.
  • addedviewWillDisappearAfter the forced rotation in mark 2 can be resolved. But tag 3 still fails. In fact, if the next interface is non-rotatable when pushing, then the direction must remain the same.

If a push or pop enters a special screen, it will be ok as long as it keeps a mandatory rotation on entry and exit, and its own shouldAutorate is YES. The key is to get out of the special interface and into the default interface. Pop will succeed, push will fail if the default interface is non-rotatable.

There are two approaches to this:

  • Rotate the current interface to default before leaving, rotate first, then push.
  • Change the default interface to rotatable.
5. Rotate to default before leaving the special directions screen

Because the orientation supported by the special interface does not contain the default orientation, it only does not work when you force the rotation. You need to change the supported orientation before forcing the rotation. Specific code:

- (IBAction)push:(id)sender { [self changeOrientationBeforeDisappear]; // Change the direction before leaving. This method is called on every other exit. TFThirdViewController *thirdVC = [[TFThirdViewController alloc] init]; [self.navigationController pushViewController:thirdVC animated:YES]; } -(void)changeOrientationBeforeDisappear{ _orientation = UIInterfaceOrientationMaskPortrait; / / replace for the default direction [UIDevice changeInterfaceOrientationTo: (UIInterfaceOrientationPortrait)]; _orientation = UIInterfaceOrientationMaskLandscapeLeft; / / replace for special direction the direction of the interface itself need} - (UIInterfaceOrientationMask) supportedInterfaceOrientations {return_orientation; // Change as variable changes}Copy the code

What happens if the next interface is not the default? There will be two rotations. When you leave, rotate to default and enter the next interface, which itself rotates to the specified direction. The effect is not good, if you want to reach the target, how to do? Will leave know expect what is the direction of the interface, and then exactly the preferredInterfaceOrientationForPresentation this intention. So change it to:

@interface TFSecondViewController (){
   UIInterfaceOrientationMask _orientation;
   UIInterfaceOrientationMask _needOrientation;
}

@end

@implementation TFSecondViewController

- (void)viewDidLoad {
   [super viewDidLoad];
   
   _needOrientation = UIInterfaceOrientationMaskLandscapeLeft;
   _orientation = _needOrientation;
}

-(void)viewWillAppear:(BOOL)animated{
   [UIDevice changeInterfaceOrientationTo:(UIInterfaceOrientationLandscapeLeft)];
}

-(BOOL)shouldAutorotate{
   returnYES; } - (IBAction)push:(id)sender { TFThirdViewController *thirdVC = [[TFThirdViewController alloc] init]; [self changeOrientationBeforeDisappearTo:thirdVC]; // Change the direction before leaving. This method is called on every other exit. Can't ` viewWillDisappear ` calls, because at times like these, has triggered a push [self. The navigationController pushViewController: thirdVC animated: YES]; } -(void)changeOrientationBeforeDisappearTo:(UIViewController *)nextVC{ _orientation = UIInterfaceOrientationMaskAll; / / to any direction [UIDevice changeInterfaceOrientationTo: [nextVC preferredInterfaceOrientationForPresentation]]. _orientation = _needOrientation; / / replace for special direction the direction of the interface itself need} - (UIInterfaceOrientationMask) supportedInterfaceOrientations {return_orientation; } @endCopy the code

_needOrientation Specifies the style required for the current page.

To sum it up:

  • For most cases, create a baseVC that sets the default orientation.
  • For special direction interface:
    • To enter (viewWillAppear) force rotation to the desired direction
    • When you leave, notice that it’s notviewWillDisappearBefore the push operation, change the direction to the expected direction of the next interface.
    • Of course selfshouldAutorotateKeep it YES.
  • All three direction-related methods need to be implemented. Because the base class (BaseVC) does the processing, most of the work is saved. Special direction of the interface can be processed individually.
  • preferredInterfaceOrientationForPresentationI want to go in the same direction as I came in, so I don’t have two rotations.

Compared to the base class shouldAutorotate to YES, the benefits of this solution are that handle the special cases of basic compression in special within the interface, only rely on other supportedInterfaceOrientations interface, this method is a complementary, Does not interfere with the original design of other interfaces. ShouldAutorotate is a bit of a hassle because other interfaces may not want torotate.

Test pop and push again:

action The current The target Target rotatable The results of
push The default special ✔ ️ successful
push special The default ✔ ️ successful
push special The default successful
pop The default special ✔ ️ successful
pop special The default ✔ ️ successful
pop special The default successful
push Special 1 Special 2 ✔ ️ successful
pop Special 1 The default 2 ✔ ️ successful
  • The special ones are rotatable, so that’s out
6. Present and dismiss
action The current The target Target rotatable The results of
present The default special ✔ ️ Melt (1)
present special The default ✔ ️ successful
present special The default successful
dismiss The default special ✔ ️ successful
dismiss special The default ✔ ️ successful
dismiss special The default successful
present Special 1 Special 2 ✔ ️ successful
dismiss Special 1 The default 2 ✔ ️ successful

Melt 1 problem because there is no implementation preferredInterfaceOrientationForPresentation, and the style of the default results is the current statusBar, from the default in the past, that is vertical, And the style of the interface supportedInterfaceOrientations is landscape, so the direction of the priority (preferredxxx) do not contain in the direction of the support (supportedxxx) collapses. , in accordance with the provisions of the previous supportedInterfaceOrientations is must implement and achieved success.

So the solution passes the test.

Finally, there is a difference between present and push: If A– >B is present and A is not rotatable but supports vertical, and B is rotatable and supports vertical, then A– >B –> rotate to landscape –> dismiss, A becomes landscape and is not rotatable.

So in dismiss, the interface that comes back doesn’t care if you can rotate, if you support the current direction, it just changes to the current direction. And supportedInterfaceOrientations default is the direction of the three, so not implement this method and use the default, when dismiss pit.