Practical analysis of iOS screen rotation

Abstract: How to realize the custom screen rotation scene more flexibly and conveniently, this article will show you the secret!

Article | Constructing an iOS App Development Team

Based on our past development experience, this article sorts out the relevant practical methods for implementing screen rotation, and analyzes the common problems encountered in the implementation process.

1. Fast rotation

The implementation of iOS screen rotation involves a bunch of enumeration values ​​and callback methods. For developers who have not done rotation-related requirements, they may feel dizzy when they come up, so let's do it first and let the screen rotate.
There are two main ways to achieve rotation, following the mobile phone induction rotation and manual rotation, and the two methods will be introduced one by one next.

Method 1: Rotate with the phone sensor

To achieve automatic rotation with the phone, first let the current view controller implement the following three methods:

/// 是否自动旋转
- (BOOL)shouldAutorotate {
    
    
    return YES;
}

/// 当前 VC支持的屏幕方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    
    
    return UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;
}

/// 优先的屏幕方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    
    
    return UIInterfaceOrientationPortrait;
}

This method needs to pay attention to the following points:

  • shouldAutorotateReturns YESthe representation 跟随系统旋转, but supportedInterfaceOrientationsaffected by the return value of the method, it only supports rotating to the supported orientation following the phone sensor.
  • preferredInterfaceOrientationForPresentationNeed to return the direction supported in supportedInterfaceOrientations, otherwise 'UIApplicationInvalidInterfaceOrientation'a crash will occur.

Method 2: Manual rotation

This method is very common in many video software, click the button to rotate to landscape.
At this time, you need to return yes in , and then pass in the direction you need to rotate to shouldAutorotatein this method . UIInterfaceOrientationNote that this is 私有方法, please use it at your own discretion.

- (void)changeVCToOrientation:(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];
    }
}

scene application

auto rotate

If your iPhone has not turned off the system screen rotation, you can find that the page of the system album APP can rotate according to the direction of the phone.
If you want to achieve the same effect as it, you only need to configure your view controller according to the previous method 1 (rotate with the phone sensor), and then the controller can be rotated freely in the supportedInterfaceOrientationsreturned direction.

Can only be rotated manually

This kind of scene is relatively rare, and a common scene in live video apps is a combination of automatic and manual rotation.

If you want to achieve a method that can only be rotated by clicking a button, you first need to supportedInterfaceOrientationsreturn the direction you need to support in the method. The focus here is on shouldAutorotatethe return value of the method.

The above method 2 (manual rotation) shows that manual rotation needs to shouldAutorotatebe returned YES, but this will also allow the controller to support it 自动旋转, which does not meet this requirement, so we handle it as follows:

- (BOOL)shouldAutorotate {
    
    
    if (self.isRotationNeeded) {
    
    
        return YES;
    } else {
    
    
        return NO;
    }
}    

Attribute isRotationNeededAs whether it is required 旋转的标记, isRotationNeeded is NO by default. Even if you rotate the device at this time, it will not return YES when calling the shouldAutorotate method, so the screen will not rotate automatically.

All that is left is that you need to set isRotationNeeded to YES after clicking the rotation button and call the manual rotation method, so that the effect of only manual rotation after processing is realized.

2. UI layout update after rotation

Usually, after the application is rotated to the horizontal and vertical screens, different aspect ratios will have different UIs, so in the screen rotation scenario, we need to solve the problem of UI adaptation after rotation.
When the phone is rotated, if shouldAutorotatereturns under normal circumstances YES, the method will be triggered when the view controller needs to be rotated, viewWillTransitionToSizeso we have found the opportunity to update the horizontal and vertical screen UI, that is, to complete the adaptation logic after rotation in the completion block.

/*
This method is called when the view controller's view's size is
changed by its parent (i.e. for the root view controller when its window rotates or is resized).

If you override this method, you should either call super to
propagate the change to children or manually forward the 
change to children.
 */
- (void)viewWillTransitionToSize:(CGSize)size
       withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    
    
    
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
    
    [coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
    
    
        //横屏:size.width > size.height
        //竖屏: size.width < size.height
        NSLog(@"旋转完成,更新布局");
    
    }];
}

3. Related issues

When developing the requirements of the rotation scene, due to the complex multi-level configuration and a large number of enumeration types, it is inevitable to encounter some problems of crashes and inability to rotate. Let's summarize such problems below.

Problem 1: Unable to auto-rotate

First check whether the system screen rotation switch is locked. After the system screen lock switch is turned on, the app cannot automatically rotate, but you can call the method mentioned above to manually rotate.
insert image description here

Problem 2: The setting of multi-level screen rotation control is wrong

The following methods can set the global permission of screen rotation:

  • Device OrientationProperty configuration: "TARGETS > General > Deployment Info > Device Orientation", the figure is the default configuration of xcode, it is worth noting that the iPhone does not support rotation to the Upside Down direction.
    insert image description here

  • Appdelegate的 supportedInterfaceOrientationsForWindow 方法:
// 返回需要支持的方向
// 如果我们实现了Appdelegate的这一方法,那么我们的App的全局旋转设置将以这里的为准
- (UIInterfaceOrientationMask)application:(UIApplication *)applicatio supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window {
    
    
    return UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskPortrait;
}

The priority of the above two methods: Appdelegate method > Target configuration, the configuration of these two methods and the supportedInterfaceOrientations method of the controller will affect the final orientation supported by the final view controller.

Taking the method of opening the controller with present in iOS 14 as an example, the screen orientation finally supported by the current controller depends on the value of the highest priority method among the above two methods, and the intersection of the supportedInterfaceOrientations of the controller.

To sum up, there are the following situations:

  • If the intersection is empty and returns YES in the controller's shouldAutorotate method, UIApplicationInvalidInterfaceOrientationthe crash will occur.
  • If the intersection is empty, and the shouldAutorotate method of the controller returns NO, and the supportedInterfaceOrientations method of the controller does not conflict with the return value of the preferredInterfaceOrientationForPresentation method (the return value of the former includes the return value of the latter), then the direction configured by the controller is displayed.
  • If the intersection is empty, and the shouldAutorotate method of the controller returns NO, and the supportedInterfaceOrientations method of the controller conflicts with the return value of the preferredInterfaceOrientationForPresentation method (the return value of the former does not include the return value of the latter), a crash will occur UIApplicationInvalidInterfaceOrientation.
  • If the intersection is not empty, the controller's supportedInterfaceOrientations method conflicts with the return value of the preferredInterfaceOrientationForPresentation method, and a UIApplicationInvalidInterfaceOrientation crash will occur.
  • If the intersection is not empty, the supportedInterfaceOrientations method of the controller does not conflict with the return value of the preferredInterfaceOrientationForPresentation method, and the current controller determines whether to automatically rotate within the direction of the intersection according to the return value of shouldAutorotate.

It is suggested here that if there is no need for global configuration, do not change the Target attribute configuration or implement the Appdelegate method, but only ViewControllerimplement the code in the above-mentioned way in the to achieve the rotation effect.

Question 3: Turn on the system lock screen switch when the screen is in landscape mode, and the view is forced to return to the portrait screen

Since iOS is closed source, of course we have no way of knowing why Apple does this, but we can use some means to avoid this problem. Fortunately, when such a rotation occurs, the system will also trigger the same method call as the normal rotation.

Taking iPhone X as an example, when the control page is opened by pulling down, we will receive the system notification of UIApplicationWillResignActiveNotification, and after closing the control page, we will receive the notification of UIApplicationDidBecomeActiveNotification. Use these two notifications to record the status, and judge whether it is Active by using shouldAutorotate Status returns YES/NO.

- (void)setupNotification {
    
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(applicationWillResignActive:)
                                                 name:UIApplicationWillResignActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(applicationDidBecomeActive:)
                                                 name:UIApplicationDidBecomeActiveNotification object:nil];
}

- (BOOL)shouldAutorotate {
    
    
    if (!self.isApplicationActive) {
    
    
            return NO;
        } else {
    
    
            return YES;
        }
    }
}

4. Relevant enumerated values

In the previous narration, we also got to know some enumeration values ​​related to screen rotation. At first glance, this piece of content really feels dazzling, but we can clearly understand the keywords in their names such as: Device、Interface, and understand its meaning where each enumeration type is used, and we can sort it out logical.

1. Device orientation: UIDeviceOrientation

UIDeviceOrientation is based on the position of the home button, which is affected by the sensor and has nothing to do with the orientation of the current screen display, so it can only be valued but not set.

typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
    
    
    UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
} API_UNAVAILABLE(tvos);

​​​​​​​​This enumeration is not directly used in the screen rotation method described above, but it becomes useful if you need to monitor the current orientation of the device. The orientation of the current device can be [UIDevice currentDevice].orientationobtained by . To monitor the orientation change of the device, you can use the following code to achieve:

 [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
 [[NSNotificationCenter defaultCenter] addObserver:observer
                                          selector:@selector(onDeviceOrientationChange:)
                                              name:UIDeviceOrientationDidChangeNotification
                                            object:nil];

2. Page orientation: UIInterfaceOrientation

UIInterfaceOrientation is the orientation of the current view controller. Different from the orientation of the device, it is the orientation that the screen is displaying. The return value of the preferredInterfaceOrientationForPresentation method is this enumeration type.

/// 优先的屏幕方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    
    
    return UIInterfaceOrientationPortrait;
}

Note that UIInterfaceOrientationLandscapeLeft corresponds to UIDeviceOrientationLandscapeRight, and the two enumeration types are opposite.

typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
    
    
    UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
} API_UNAVAILABLE(tvos);

3. Page orientation: UIInterfaceOrientationMask

Observing the value of the UIInterfaceOrientationMask enumeration, we will find that this is a type defined to support multiple UIInterfaceOrientations, which is used as the return value of the supportedInterfaceOrientations method. For example, if we return UIInterfaceOrientationMaskAll in this method, all directions can be supported.

/// 当前 VC支持的屏幕方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    
    
    return UIInterfaceOrientationMaskAll;
}
typedef NS_OPTIONS(NSUInteger, UIInterfaceOrientationMask) {
    
    
    UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait),
    UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft),
    UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight),
    UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown),
    UIInterfaceOrientationMaskLandscape = (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
    UIInterfaceOrientationMaskAll = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown),
    UIInterfaceOrientationMaskAllButUpsideDown = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
} API_UNAVAILABLE(tvos);

V. Conclusion

ZEGO RoomKit SDK currently supports screen rotation scenarios, and in the form of JSON configuration in version 2.0.0, it supports more flexible and convenient implementation of custom screen rotation scenarios.
Screen rotation is often an inseparable link in live video APPs. Sort out the meaning of the above three enumerations, as well as the timing of calling the rotation method, and refresh the rotated layout at the right time. iOS rotation is suitable Matching is no longer difficult.

Guess you like

Origin blog.csdn.net/zhanglei5415/article/details/131391551