Caution is the best way to catch cicadas forever. — Quotations from Zhuangzi

preface

Direction rotation is basically used in daily App, and may be mainly used to realize functions due to the deadline. I seriously thought about why to do this or whether there are any bugs or whether it will be used in the subsequent development. It’s not just the screen rotation, it’s other things as well, which makes it feel good when you’re doing it, but when you’re actually developing it, sometimes it feels like a blackout.

The purpose of this article is to deepen understanding and consolidate the knowledge of memory, in addition to this article also has some understanding of the screen rotation and some points to pay attention to, you can never be too careful.

Enumeration in three directions

  1. UIDeviceOrientation: describes the orientation of the device and contains the following values:

    UIDeviceOrientationUnknown UIDeviceOrientationPortrait, / / erect UIDeviceOrientationPortraitUpsideDown, / / the vertical UIDeviceOrientationLandscapeLeft, landscape, / / home button on the right side UIDeviceOrientationLandscapeRight, / / landscape, The home button on the left UIDeviceOrientationFaceUp, / / screen up UIDeviceOrientationFaceDown / / screen down The UIDeviceOrientationLandscapeLeft can understand like this: With the home button or the home Indicator (only with the home button to describe the same below) for reference, UIDeviceOrientationLandscapeRight is rotated to the right equipment, so the home button on the left side. And vice versa.Copy the code
  2. UIInterfaceOrientation: describes the orientation of the page and has the following values:

    UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown, UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait, UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown, UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight, UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft UIInterfaceOrientationLandscapeLeft UIDeviceOrientationLandscapeRight and equivalence, which is because the rotation device, page to the opposite direction of rotation is needed to conform to the normal use of specification.Copy the code
  3. UIInterfaceOrientationMask: role is in the specified view controller support page collection of a variety of directions, contains the following values:

    UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait),
    UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft),
    UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight),
    UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown),
    
    UIInterfaceOrientationMaskLandscape = (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
    
    UIInterfaceOrientationMaskAll = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown),
    
    UIInterfaceOrientationMaskAllButUpsideDown = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
    Copy the code

The first enumeration is used to describe the direction of the device; The second enumeration describes the direction of the page; The third enumeration describes the set of directions the page can support. In addition, the device face up and face down are usually not considered.

Gets the direction of the device

  1. The orientation of the current device can be determined by obtaining the orientation attribute value of the device.

  2. Sometimes you need to listen to get the direction of the device. You need to use notifications to listen, but it is important to call a pair of methods before and after getting the data from notifications of device direction changes: BeginGeneratingDeviceOrientationNotifications and endGeneratingDeviceOrientationNotifications

    // By notification name: UIDeviceOrientationDidChangeNotification to obtain / / call generating equipment direction change notification method [[NSNotificationCenter defaultCenter] beginGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeOrientation) name:UIDeviceOrientationDidChangeNotification object:nil]; / /... / / remove notice need to call the end of generating equipment direction change notice [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter] removeObserver:self];Copy the code
  3. One important thing to note is that if the user locks the screen orientation in the control bar, they will not receive notification of the orientation change, including forcing the first orientation even if the controller you are in supports multiple directions.

  4. Another problem with the status bar is that if you don’t set the status style, it will hide the status bar when you force it to landscape.

  5. Also use this way may receive state UIDeviceOrientationUnknown, such as the first time into the process of app and only support portrait, but usually jump a few times when the direction of the callback, when in this state can return to a state of vertical screen.

Several ways of setting device rotation

  1. Because the device supports rotation in several ways
    • Project project Settings: The Settings are the same in the project General and info.plist because the data is stored in info.plist
    • Settings in the AppDelegate: Through the agent – (UIInterfaceOrientationMask) application: (UIApplication *) application SupportedInterfaceOrientationsForWindow: (UIWindow *) the direction of the window set support.
    • The setting of a single view controller requires that its parent controller, UITabBarController or UINavigationController, also supports the direction of the current controller.
  2. The above Settings can be simply understood as follows: The project Settings are the default Settings of App; The Settings in the AppDelegate are the ones you’re currently setting, so they override the project’s Settings; The individual view controller Settings are the current page Settings, but are influenced in the AppDelegate.
  3. Directions other than Portrait Upside Down will be checked by default when creating an app.
  4. Even if you don’t check any of them, the app will default to Portrait.

Application scenario analysis of equipment rotation

Scenario analysis
  1. In daily development, we are more or less exposed to the following situations:
    • The landscape screen is mandatory for some pages, and only portrait screen is displayed on other pages
    • For some pages, the landscape screen is mandatory. For some pages, the rotation can be automatically based on the device. For other pages, the portrait screen is mandatory.
    • Pages are portrait or landscape (not discussed)
  2. From the above, we need to support the need for forced orientation or auto-rotation of individual pages.
Solution to force landscape screen
  1. We know that most pages are Portrait, so we should only check Portrait for the supported orientation.

  2. Settings can not be set in the AppDelegate or only return UIInterfaceOrientationMaskPortrait, will be in the default vertical screen display.

  3. We need compulsory in portrait or landscape page rewrite shouldAutorotate and supportedInterfaceOrientations, and the value of the former should be YES, otherwise there may be unable to transfer problem.

  4. Here is the code that forces the device to rotate to the left and then go landscape:

    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:@selector(setOrientation:)]];
    invocation.selector = NSSelectorFromString(@"setOrientation:"); invocation.target = [UIDevice currentDevice]; int initOrientation = UIDeviceOrientationLandscapeLeft; // The invocation we need here is the device direction valuesetArgument:&initOrientation atIndex:2]; [invocation invoke]; / * * the following two lines of code to the current navigation bar or bar at the bottom of the direction of rotation to equipment * / [UINavigationController attemptRotationToDeviceOrientation]; [UITabBarController attemptRotationToDeviceOrientation];Copy the code
  5. If the page is in UINavigationController or UITabBarController, write a category to the navigation bar or bottom bar or override two methods in the custom class: ShouldAutorotate and supportedInterfaceOrientations. Value to the current view controller, can through self. VisibleViewController tried to the controller gets the current display, further access to the required value.

  6. If automatic screen rotation is supported when you force landscape or portrait, you need to confirm whether the orientation lock of the phone is turned on. If turned on, it will not be able to rotate to the supported orientation.

  7. Good need to return to the previous page Settings, otherwise it may appear push a new page to landscape, pop back to find that the previous page has also become landscape.

  8. The last point is that after dealing with the above problems, page A is portrait and page B is landscape. After pushB, the animation is very ugly when returning to A page. For better aesthetics, write A transition animation to avoid this visual bug.

Solution Analysis

To sum up, it is found that if several pages need to be set, then it is very troublesome to set separately. It is best to have a function or a property to configure, but because the Settings of each page are different, the state needs to be stored, so the property is better. By adding an attribute through classification, you can set the supported direction, sometimes as long as strong rotation, that is, a single direction, and multiple directions, so that it can be rotated to the supported direction, so only one attribute is needed, and this attribute can accept multiple values, System of enumeration UIInterfaceOrientationMask landscape page direction is the opposite direction and equipment, good for the latter, creates a displacement enumeration, including composite value.

@interface UIViewController (Rotate) /** Rotate only one direction. */ @Property (Assign, Nonatomic) OMRotateOrientation omSupportOrientations; */ assign (Assign, Nonatomic) OMRotateOrientation omSupportOrientations; @endCopy the code

In order for the page to rotate normally, its parent controller must also support the current page direction, which can be simply rewritten by classification:

#pragma mark- UINavigationController
@implementation UINavigationController (Rotate)

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

- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
    return self.visibleViewController.supportedInterfaceOrientations;
}

@end

#pragma mark- UITabBarController
@implementation UITabBarController (Rotate)

- (BOOL)shouldAutorotate{
    if ([self.selectedViewController isKindOfClass:[UINavigationController class]]) {
        return ((UINavigationController *)self.selectedViewController).visibleViewController.shouldAutorotate;
    }
    return self.selectedViewController.shouldAutorotate;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
    if ([self.selectedViewController isKindOfClass:[UINavigationController class]]) {
        return ((UINavigationController *)self.selectedViewController).visibleViewController.supportedInterfaceOrientations;
    }
    return self.selectedViewController.supportedInterfaceOrientations;
}

@end
Copy the code

Use a simple assignment:

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    self.omSupportOrientations = OMRotateOrientationLandscape;
}
Copy the code

It’s ok to find a bottom bar nested in a navigation bar or any other kind of nesting in a usage scenario, whether it’s push or Present, or tabbar’s home page, or a new navigation bar. The only bug is that if you only have the bottom bar, the first view controller if you put it in viewWillAppear will make the bottom bar invisible, if you just have the bottom bar, Can click method in UITabBarControllerDelegate set vc support direction and in the bar at the bottom of the first page of viewDidLoad set can be the direction of the support, to ensure that the app when transferred to specify the direction, However, it seems that there are no different directions for each page in the bottom bar of the main page.

When jumping to a new page in portrait while in landscape, the new page will not be portrait, but still landscape. Solution: If the direction of the new push page is different from that of the current page, you need to set the supported direction of the new page (even if the direction of the new page is vertical).

Achieved effect

When you have a bottom bar and a navigation bar

When only tabBar:

Demo address: github.com/oymuzi/OMRo…

conclusion

The said all said, and then shiver is the use of strong turn when the need to write a turn animation, or return to the time is very ugly. Setting the status bar calls the API directly, and that’s it.