现在看到的很多工程的方向代码十分杂乱,用得都是旧版被deprecated的方法,所以开一贴来总结iOS中Orientation
本文章学习于:http://www.molotang.com/articles/1530.html
Xcode工程配置中Target->General->Deployment Info中有对应用的屏幕方向的控制
而在info.plist中对应着的参数配置如下:
支持的选项:
对应XML是:
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
大致的分为下面几种对屏幕控制的方法:
1.Window层级别:
在iOS6以后AppDelegate类的实现里多了一个关于UIWindow直接地的屏幕控制方法:
@property (nonatomic, retain) UIWindow *window NS_AVAILABLE_IOS(5_0);
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window NS_AVAILABLE_IOS(6_0);
这个方法提供了一个直接地对UIWindow的操作,如果没有实现这个方法的话,会默认使用target或者info.plist的屏幕方向设置。
测试一下:
将Target相关项设置成Portrait,并在上述方法实现的返回值为:UIInterfaceOrientationMaskLandscape
如图:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return UIInterfaceOrientationMaskLandscape;
}
在方法中设立断点,然后观察:
点击 ' Continue program execution ' 如图:
可以很清楚地看到,window从nil到有具体值,并且实际情况是再变为nil值,再被赋予一个(同栈或者同堆,因为结构相似)的地址,然后变化几次最后结束方法,同理于如果把方法设置成竖屏,将Target设置成横屏,现象和效果都一样。
但是设置如下:
方法实现如下;
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return UIInterfaceOrientationLandscapeLeft;
}
一运行就会直接崩溃,并且崩溃日志如下:
网上关于这中横竖屏的错误一般发生于对横竖屏很敏感的情况,例如用到摄像头,UIImageView等等情况。
关于这个错误7月26日解决:因为UIInterfaceOrientation 与 UIInterfaceOrientationMask 两个属于完全不同的枚举值(千万注意有一个Mask的区别啊),下面有列出
2.Controller层级别:
非常多的工程很常见的一个方法:
// Applications should use supportedInterfaceOrientations and/or shouldAutorotate..
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation NS_DEPRECATED_IOS(2_0, 6_0);
其实在6.1以后已经被deprecate了,也不多说,就是返回一个布尔值决定了视图控制器是否支持制定的方向。 别的自行百度,建议少用。
// New Autorotation support.
- (BOOL)shouldAutorotate NS_AVAILABLE_IOS(6_0);
- (NSUInteger)supportedInterfaceOrientations NS_AVAILABLE_IOS(6_0);
6.1以后提供了这两个方法来代替上面被弃用的方法,原理十分简单,先判断能否支持旋转,再判断支持具体的哪个方法。
注意到这个参数:UIInterfaceOrientation
typedef enum : NSInteger {
UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown,
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait,
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
} UIInterfaceOrientation;
还有一个也常见的参数:UIInterfaceOrientationMask:
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),
};
是一个枚举类型。
6.1同时还提供了一个新的方法:
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation NS_AVAILABLE_IOS(6_0);
这个方法挺好玩的,官方文档里面描述的是:
You implement this method when your view controller supports two or more orientations but the content appears best in one of those orientations.
你实现这个方法当你的视图控制器支持多个方向但是如果这个内容更倾向于某个方法。如果有兴趣可以测试一下,如果不实现supportedInterfaceOrientations方法会没有效果,如果不实现代理中的方法 application:supportedInterfaceOrientationsForWindow: 会直接崩掉,可以不实现shouldAutorotate,因为默认为YES,下面给出一个例子:
代理中的方法实现:这里注意最好设置成MaskAll,不然成了非法约束会崩
#pragma mark - Orientation
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return UIInterfaceOrientationMaskAll;
}
视图控制器的方法实现:
//系统默认为YES
//- (BOOL)shouldAutorotate
//{
// return YES;
//}
- (NSUInteger)supportedInterfaceOrientations
{
return (UIInterfaceOrientationLandscapeLeft | UIInterfaceOrientationLandscapeRight | UIInterfaceOrientationPortrait);
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
现象是先横屏,然后迅速转成竖屏,很容易明白对应的设置和实现,在Target中设置成水平方法,但是提供了和能够完成方向有多个,然而在preferredInterfaceOrientationForPresentation方法才是最终的屏幕方向。
获取屏幕方向的方法:
1.UIViewController的屏幕方向属性:
@property(nonatomic,readonly) UIInterfaceOrientation interfaceOrientation NS_DEPRECATED_IOS(2_0,8_0);
具体用法:
NSLog(@"SELF rotation : %ld", (long)self.interfaceOrientation);
2.UIApplication的状态栏方向属性:
@property(nonatomic) UIInterfaceOrientation statusBarOrientation;
具体用法:
NSLog(@"UIApplication sharedApplication: %ld", (long)[[UIApplication sharedApplication] statusBarOrientation]);
可能有人会问,为什么有了视图控制器还要这个属性呢,其实这个指的是应用的状态栏方向,具体可以自己弄个测试Demo,就如上面测试preferredInterfaceOrientationForPresentation这个方法的时候,如果两个都输出,viewController的值为1,而application的值为4
3.UIDevice的当前设备方向属性:
@property(nonatomic,readonly) UIDeviceOrientation orientation; // return current device orientation. this will return UIDeviceOrientationUnknown unless device orientation notifications are being generated.
具体用法:
NSLog(@"UIDevice currentDevice : %ld", [[UIDevice currentDevice] orientation]);
在同一个Demo同时输出:
而关于这几个方向的不同点前辈已经找到了一个很好的帖子:
http://stackoverflow.com/questions/7968451/different-ways-of-getting-current-interface-orientation
关于旋转状态的方法:
// Notifies when rotation begins, reaches halfway point and ends.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration NS_DEPRECATED_IOS(2_0,8_0, "Implement viewWillTransitionToSize:withTransitionCoordinator: instead");
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation NS_DEPRECATED_IOS(2_0,8_0);
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration NS_DEPRECATED_IOS(3_0,8_0, "Implement viewWillTransitionToSize:withTransitionCoordinator: instead");
- (void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration NS_DEPRECATED_IOS(2_0, 5_0);
- (void)didAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation NS_DEPRECATED_IOS(2_0, 5_0); // The rotating header and footer views are offscreen.
- (void)willAnimateSecondHalfOfRotationFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation duration:(NSTimeInterval)duration NS_DEPRECATED_IOS(2_0, 5_0); // A this point, our view orientation is set to the new orientation.
具体用法就不详述了,也很简单
有些应用只支持单向的横屏,但是却要用到竖屏该如何处理?
/*
* 游戏为 横屏 时,在根控制器中,添加下面的 3个方法,限制游戏的方向为 横屏
* 这么设置的原因在于:银联iPhone 版本,需要支持Portrait 方向,若游戏是横屏,就需要勾选 Portrait、Landscape Left、Landscape Right
采用下面的3个方法就能起到在支持Portrait 方向的同时,不影响游戏本身的布局方向
*/
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
toInterfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
- (BOOL)shouldAutorotate
{
return YES;
}
有一种技巧,在Build Settings设置都支持,而在要用到的地方调用方向限制的方法。
关于屏幕方向的难点还在于容器视图控制器的设置UITarbarController 和 UINavigationController 的实现上,将会在Orientation - 2中详细阐述,包括如果继承使用和delegate实现。
用宏来控制游戏方向:
#if defined(GAME_ORIENTATION_MASK_PORTRAIT)
UIInterfaceOrientationMask orientation = UIInterfaceOrientationMaskAllButUpsideDown;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
orientation = UIInterfaceOrientationMaskPortraitUpsideDown;
}
#else
UIInterfaceOrientationMask orientation = UIInterfaceOrientationMaskLandscape;
#endif