基础概念
UIDeviceOrientation
UIDeviceOrientation,表明设备朝向,能够经过[UIDevice currentDevice] orientation]
获取,取值有:
typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
UIDeviceOrientationUnknown, // 不知道,发动时会出现
UIDeviceOrientationPortrait, // 竖屏,home键在底部
UIDeviceOrientationPortraitUpsideDown, // 倒立,home键在顶部
UIDeviceOrientationLandscapeLeft, // 左横屏,home键在右边
UIDeviceOrientationLandscapeRight, // 右横屏,home键在左面
UIDeviceOrientationFaceUp, // 屏幕朝上
UIDeviceOrientationFaceDown // 屏幕朝下
}
UIInterfaceOrientation
UIInterfaceOrientation,表明页面内容朝向,留意UIInterfaceOrientation和UIDeviceOrientation的联络,其中UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,这是由于:
This is because rotating the device to the left requires rotating the content to the right.
不用特别细究两者之间联络,咱们只需求依据需求设置好UIInterfaceOrientation即可,经过
[UIApplication shareApplication] statusBarOrientation]
能够获取当时状态栏朝向。
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown,
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait,
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
};
UIInterfaceOrientationMask
UIInterfaceOrientationMask,是由页面内容朝向的二进制偏移组成,用来更方便描绘某个界面支撑的朝向。比方说下面的UIInterfaceOrientationMaskLandscape,其实便是由MaskLandscapeLeft和MaskLandscapeRight组成,这样能够方便描绘设备支撑两个横屏方向。
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),
};
UIViewController相关
UIViewController关于反正屏的三个办法:
- shouldAutorotate,页面是否答应主动旋转,被弃用api:
-shouldAutorotateToInterfaceOrientation
的取代者;默认值为YES,表明当时界面答应跟随设备旋转而主动旋转; - supportedInterfaceOrientations,该界面支撑的界面朝向,能够回来四个朝向的恣意组合,iPad默认值是四个朝向都支撑,iPhone默认值是除了UpsideDown的三个朝向。这个办法回调的条件是shouldAutorotate=YES。
- preferredInterfaceOrientationForPresentation,该界面被present出来的界面朝向,能够回来四个朝向的恣意组合。假定没有回来,则present时和本来界面坚持一致。
AppDelegate相关
AppDelegate的supportedInterfaceOrientationsForWindow办法,依据需求回来当时window是否支撑横屏。
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window;
工程装备相关
在xcode的工程设置的General能够装备iPhone和iPad的页面朝向支撑。
反正屏切换实例
竖屏界面怎么present横屏界面
竖屏present横屏是很遍及的场景,比方说视频播映场景的全屏切换,就能够在当时竖屏的界面present一个横屏播映界面的办法,完成反正屏切换。具体的操作过程只需求两步:
1,设置modalPresentationStyle为UIModalPresentationFullScreen; 2、preferredInterfaceOrientationForPresentation办法,回来UIInterfaceOrientationLandscapeRight;
比方说下面的LandscapeViewController界面:
// 点击时设置
- (void)onClick {
LandscapeViewController *landVC = [[LandscapeViewController alloc] init];
landVC.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:landVC animated:YES completion:nil];
}
// LandscapeViewController内部代码
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeRight;
}
思考:
1、假定是横屏转竖屏呢? 2、假定想要自定义旋转作用完成呢?(UIViewControllerAnimatedTransitioning协议)
竖屏界面怎么push横屏界面
比方说这样的场景:App的rootVC是navigationVC,导航栈内先有一个竖屏界面,现在想要push一个横屏界面LandscapeViewController。
一个简单的办法如下:
// appdelegate完成
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
if ([self.navigationVC.topViewController isKindOfClass:LandscapeViewController.class]) {
return UIInterfaceOrientationMaskLandscapeRight;
}
else {
return UIInterfaceOrientationMaskPortrait;
}
}
// LandscapeViewController内部完成
- (void)viewDidLoad {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:@selector(setOrientation:)]];
invocation.selector = NSSelectorFromString(@"setOrientation:");
invocation.target = [UIDevice currentDevice];
int initOrientation = UIDeviceOrientationLandscapeRight;
[invocation setArgument:&initOrientation atIndex:2];
[invocation invoke];
}
思考:
1、这儿为什么没有用到UIViewController的三个办法?
2、在viewDidLoad调用的旋转办法是什么意思?
横屏竖切换机制剖析
前面的实例介绍了怎么支撑切换,可是也产生一些疑问:
工程装备文件也没有设置横屏,为什么后边就能支撑横屏?
工程装备、AppDelegate、UIViewController这三者,在反正屏切换过程的联络是什么?
主动旋转和手动旋转有什么区别?
….
只是知道切换适配代码,是无法构成反正屏切换了解,也就很难答复上述的问题。
由于没有找到解说反正屏切换机制的官方文档,以下依据自己的经历对这个切换的机制进行剖析。
体系怎么知道App对界面朝向的支撑
这儿分两种状况,App发动前和App运行时。
App发动前
在App发动前进程还未加载,代码无法运行,体系必定无法经过AppDelegate或者UIViewController这种代码的办法获取反正屏的装备。所以在这种状况下,工程装备中的plist描绘App对屏幕的适配,就能够很好帮助体系辨认应该以什么样的朝向发动App。
所以在plist中增加横屏的支撑,优点是开屏能够支撑横屏,这样界面展示愈加顺滑;坏处也是开屏支撑了横屏,导致开屏为横屏发动的时分,UIScreen的mainScreen是横屏的巨细,但许多业务逻辑代码都会以[UIScreen mainScreen]去取屏幕宽度和高度,所以很简单取到过错的值。
App运行时
当App进程加载完成,此刻体系能够经过运行时询问的办法,来动态获取不同机遇的界面朝向。
此刻AppDelegate操控的是UIWindow层面的朝向,UIViewController操控的是VC层面的朝向。需求留意的是,当咱们回来UIViewController的朝向时,还要考虑父容器的朝向。一般一个App的界面层级是UIWindow=>RootViewController(容器vc)=>UIViewController(界面vc)。假定只在UIWindow回来界面朝向也是答应的,就如同上面的实例剖析中的push横屏。
不同界面的朝向操控
还是假定UIWindow=>RootViewController(容器vc)=>UIViewController(界面vc)的层级,且当时ViewController是竖屏vc,现在需求push一个横屏界面LandscapeViewController。 在每次界面切换的时分,体系都会回调确认新的界面朝向,最终成果为UIWindow朝向、容器vc朝向、界面vc朝向三者的“与”值。那么假定这个值抵触了呢? 假定supportedInterfaceOrientationsForWindow一向回来的竖屏,那么后边VC设置横屏不会生效; 类似,假定UIWindow设置的是横屏,那么后边VC设置竖屏也不会生效; 假定在界面切换的过程中发现回来的朝向值未确定,体系更倾向于坚持当时朝向不变,并且可能会遇到以下的crash。
Terminating app due to uncaught exception 'UIApplicationInvalidInterfaceOrientation', reason: 'Supported orientations has no common orientation with the application, and [LandscapeViewController shouldAutorotate] is returning YES'
这个原则相同适用于当回来多个成果,比方说当时界面是竖屏,然后UIWindow和ViewController的界面朝向都支撑横屏和竖屏都支撑,那么此刻会坚持竖屏。
一种比较常用的设计:
// AppDelegate
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return self.navigationVC.topViewController.supportedInterfaceOrientations;
}
// NavigationController
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return self.topViewController.supportedInterfaceOrientations;
}
// LandscapeViewController
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeRight;
}
主动旋转和手动旋转
主动旋转指的是咱们旋转物理设备时,体系会触发界面的旋转。当咱们从一个竖屏界面push一个横屏界面时,即便横屏界面设置了shouldAutorotate=YES,这个界面也不会变成横屏,可是拿起来设备左右翻转的时分,会发现跟着设备旋转,界面也从横屏变成了竖屏。这便是主动旋转。
手动旋转指的是手动触发旋转,依据经历发现有两个api,UIViewController的+attemptRotationToDeviceOrientation
,还有UIDevice的setOrientation:
能够调整界面朝向。前者是将界面朝向对齐设备朝向,是规范api;后者是调整设备朝向,是私有api。
假定咱们在许多个竖屏界面中,需求强制横屏某一个界面,假定是子界面能够使用present的办法,假定是push那么就必须要用到这个私有api。
留意事项
其他反正屏适配办法
1、视图适配:经过transform修改layer从而在视图上完成横屏,可是此刻屏幕宽度、状态栏、安全间隔等都保存竖屏状态,这种办法只是适用于横屏弹窗等部分场景;
2、新建Window:由于App的适配是UIWindow为单位,那么理论上是能够新建一个UIWindow来横屏的界面;
反正屏切换告诉
NSNotification告诉
[[NSNotificationCenter defaultCenter] addObserverForName:UIDeviceOrientationDidChangeNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
NSLog(@"NSNotification:%@, orientation:%d", note.userInfo, [(UIDevice *)note.object orientation]);
}];
UIViewController回调
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator API_AVAILABLE(ios(8.0));
[北京/广州/深圳] 抖音番茄小说客户端团队,欢迎联络(有加必回)
/post/714156…