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:
shouldAutorotate
ReturnsYES
the representation跟随系统旋转
, butsupportedInterfaceOrientations
affected by the return value of the method, it only supports rotating to the supported orientation following the phone sensor.preferredInterfaceOrientationForPresentation
Need 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 shouldAutorotate
in this method . UIInterfaceOrientation
Note 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 supportedInterfaceOrientations
returned 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 supportedInterfaceOrientations
return the direction you need to support in the method. The focus here is on shouldAutorotate
the return value of the method.
The above method 2 (manual rotation) shows that manual rotation needs to shouldAutorotate
be 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 isRotationNeeded
As 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 shouldAutorotate
returns under normal circumstances YES
, the method will be triggered when the view controller needs to be rotated, viewWillTransitionToSize
so 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.
Problem 2: The setting of multi-level screen rotation control is wrong
The following methods can set the global permission of screen rotation:
Device Orientation
Property 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.
- 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,
UIApplicationInvalidInterfaceOrientation
the 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 ViewController
implement 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].orientation
obtained 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.