横竖屏切换那些事

采用系统提供的方法来进行横竖屏切换

一、监听屏幕旋转方向

在处理iOS横竖屏时,经常会和UIDeviceOrientation、UIInterfaceOrientation和UIInterfaceOrientationMask这三个枚举类型打交道,它们从不同角度描述了屏幕旋转方向。

UIDeviceOrientation:设备方向

iOS的设备方向是通过iOS的加速计来获取的。

1) iOS定义了以下七种设备方向

typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
    UIDeviceOrientationUnknown,                 // 未知方向,可能是设备(屏幕)斜置
    UIDeviceOrientationPortrait,                // 设备(屏幕)直立
    UIDeviceOrientationPortraitUpsideDown,      // 设备(屏幕)直立,上下顛倒
    UIDeviceOrientationLandscapeLeft,           // 设备(屏幕)向左横置
    UIDeviceOrientationLandscapeRight,          // 设备(屏幕)向右橫置
    UIDeviceOrientationFaceUp,                  // 设备(屏幕)朝上平躺
    UIDeviceOrientationFaceDown                 // 设备(屏幕)朝下平躺
};

说明:UIDeviceOrientation参考home键方向,如:home方向在右,设备(屏幕)方向向左(UIDeviceOrientationLandscapeLeft)

2)读取设备方向

UIDevice单例代表当前的设备。从这个单例中可以获得的信息设备,如设备方向orientation。

UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;

3) 监听、处理和移除 设备方向改变的通知

当设备方向变化时候,发出UIDeviceOrientationDidChangeNotification通知;注册监听该通知,可以针对不同的设备方向处理视图展示。

//开启和监听 设备旋转的通知(不开启的话,设备方向一直是UIInterfaceOrientationUnknown)
if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) {
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
}
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(handleDeviceOrientationChange:) 
                                     name:UIDeviceOrientationDidChangeNotification object:nil];
//设备方向改变的处理
- (void)handleDeviceOrientationChange:(NSNotification *)notification{
    UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
    switch (ddeviceOrientation) {
        case UIDeviceOrientationFaceUp:
            NSLog(@"屏幕朝上平躺");
            break;
        case UIDeviceOrientationFaceDown:
            NSLog(@"屏幕朝下平躺");
            break;
        case UIDeviceOrientationUnknown:
            NSLog(@"未知方向");
            break;
        case UIDeviceOrientationLandscapeLeft:
            NSLog(@"屏幕向左横置");
            break;
        case UIDeviceOrientationLandscapeRight:
            NSLog(@"屏幕向右橫置");
            break;
        case UIDeviceOrientationPortrait:
            NSLog(@"屏幕直立");
            break;
        case UIDeviceOrientationPortraitUpsideDown:
            NSLog(@"屏幕直立,上下顛倒");
            break;
        default:
            NSLog(@"无法辨识");
            break;
    }
}
//最后在dealloc中移除通知 和结束设备旋转的通知
- (void)dealloc{
    //...
    [[NSNotificationCenter defaultCenter]removeObserver:self];
    [[UIDevice currentDevice]endGeneratingDeviceOrientationNotifications];
}

说明:手机锁定竖屏后,UIDeviceOrientationDidChangeNotification通知就失效了。

2、UIInterfaceOrientation:界面方向

界面方向是反应iOS中界面的方向,它和Home按钮的方向是一致的。

1) iOS定义了以下五种界面方向

typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
    UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,       //未知方向
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,               //界面直立
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,  //界面直立,上下颠倒
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,   //界面朝左
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft    //界面朝右
} __TVOS_PROHIBITED;

说明:从定义可知,界面方向和设别方向有对应关系,如界面的竖直方向就是 设备的竖直方向:UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown

2) 读取界面方向

UIInterfaceOrientation和状态栏有关,通过UIApplication的单例调用statusBarOrientation来获取

 UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];

3) 监听、处理和移除 界面方向改变的通知

当界面方向变化时候,先后发出UIApplicationWillChangeStatusBarOrientationNotification和 UIApplicationDidChangeStatusBarOrientationNotification通知;注册监听这两个通知,可以针对不同的界面方向处理视图展示。

//以监听UIApplicationDidChangeStatusBarOrientationNotification通知为例
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(handleStatusBarOrientationChange:) 
                                     name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
//界面方向改变的处理
- (void)handleStatusBarOrientationChange: (NSNotification *)notification{
    UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    switch (interfaceOrientation) {
        case UIInterfaceOrientationUnknown:
            NSLog(@"未知方向");
            break;
        case UIInterfaceOrientationPortrait:
            NSLog(@"界面直立");
            break;
        case UIInterfaceOrientationPortraitUpsideDown:
            NSLog(@"界面直立,上下颠倒");
            break;
        case UIInterfaceOrientationLandscapeLeft:
            NSLog(@"界面朝左");
            break;
        case UIInterfaceOrientationLandscapeRight:
            NSLog(@"界面朝右");
            break;
        default:
            break;
    }
}
//最后在dealloc中移除通知
- (void)dealloc{
    //...
    [[NSNotificationCenter defaultCenter]removeObserver:self];
    [[UIDevice currentDevice]endGeneratingDeviceOrientationNotifications];
}

说明:手机锁定竖屏后,UIApplicationWillChangeStatusBarOrientationNotification和 UIApplicationDidChangeStatusBarOrientationNotification通知也失效了。

3. UIInterfaceOrientationMask

UIInterfaceOrientationMask是为了集成多种UIInterfaceOrientation而定义的类型,和ViewController相关,一共有7种

1) iOS中的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),
} __TVOS_PROHIBITED;

2) UIInterfaceOrientationMask的使用

在ViewController可以重写- (UIInterfaceOrientationMask)supportedInterfaceOrientations方法返回类型,来决定UIViewController可以支持哪些界面方向。

//支持界面直立
- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskPortrait;
}

总结:UIDeviceOrientation(设备方向)和UIInterfaceOrientation(屏幕方向)是两个不同的概念。前者代表了设备的一种状态,而后者是屏幕为了应对不同的设备状态,做出的用户界面上的响应。在iOS设备旋转时,由UIKit接收到旋转事件,然后通过AppDelegate通知当前程序的UIWindow对象,UIWindow对象通知它的rootViewController,如果该rootViewController支持旋转后的屏幕方向,完成旋转,否则不旋转;弹出的ViewController也是如此处理。

二、视图控制器中旋转方向的设置

关于禁止横屏的操作(不建议)

比较常规的方法有两种。

方法1:在项目的General–>Deployment Info–>Device Orientation中,只勾选Portrait(竖屏)

图1

方法2:Device Orientation默认设置,在Appdelegate中实现supportedInterfaceOrientationsForWindow:只返回UIInterfaceOrientationMaskPortraitt(竖屏)

-  (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window  {  
     return UIInterfaceOrientationMaskPortrait;  
}

说明:极少的APP中所有界面都是竖屏的,因为总会有界面需要支持横屏,如视频播放页。所以不建议设置禁止APP页面横屏。下面介绍如何让项目中的 视图控制器中旋转方向的设置:

APP支持多个方向
图2

说明:如此,APP支持横屏和竖屏了,但是具体视图控制器支持的页面方向还需要进一步处理。由于不支持竖屏颠倒(Upside Down),即使设备上下颠倒,通过API也不会获得设备、屏幕上下颠倒方向的。

支持ViewController屏幕方向设置

1)关键函数

视图控制器支持的界面方向主要由以下三个函数控制:

//是否自动旋转,返回YES可以自动旋转,返回NO禁止旋转  
- (BOOL)shouldAutorotate NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;  
//返回支持的方向  
- (UIInterfaceOrientationMask)supportedInterfaceOrientations NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;  
//由模态推出的视图控制器 优先支持的屏幕方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;

2)项目的跟视图控制器是导航(UINavigationController)

方法一:分别在跟导航控制器和viewController中进行设置

  • 在跟导航控制器中设置最后一个push的viewController的三个对应函数

这种方式,主要是用在,当push到某个视图控制器时,要求他能够支持自动旋转,或者强制转为横屏;这里主要用到了导航的viewControllers属性,这是一个数组,数组的最后一个元素,就是最后push进去的viewController,所以,实现代码如下:

- (BOOL)shouldAutorotate {  
    return [[self.viewControllers lastObject] shouldAutorotate];  
}  

#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0  
- (NSUInteger)supportedInterfaceOrientations  
#else  
- (UIInterfaceOrientationMask)supportedInterfaceOrientations  
#endif  
{  
     return [[self.viewControllers lastObject] supportedInterfaceOrientations];  
}  

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {  

    return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];  
}  

到这里还没有结束,这里只是获取到了最后一个push的viewController,然后用它去调用了相应的方法,具体的方法实现,还是需要在哪个viewController里进行设置的

  • 在需要特殊设置的viewController里重写上面的方法:
-(BOOL)shouldAutorotate {
    return YES;
}

#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0
- (NSUInteger)supportedInterfaceOrientations
#else
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
#endif
{
    return UIInterfaceOrientationMaskPortraitUpsideDown;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {

    return UIInterfaceOrientationPortraitUpsideDown;
}

方法二:直接在根导航控制器设置指定的控制器

第一种方法是,只要push进去一个视图控制器,都可以进行相应的设置,而这种方法是只针对特定的控制器进行设置,例如这里,我需要把SecondViewController单独设置为支持横屏,只需要将第一种方法中的导航里的相应代码做如下修改:

-(BOOL)shouldAutorotate
{
    if ([[self.viewControllers lastObject]isKindOfClass:[SecondViewController class]]) {
        return YES;
    }

    return NO;
}

#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0
- (NSUInteger)supportedInterfaceOrientations
#else
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
#endif
{
    if ([[self.viewControllers lastObject]isKindOfClass:[SecondViewController class]]) {
                return UIInterfaceOrientationMaskLandscapeLeft;
            }

    return UIInterfaceOrientationMaskPortrait;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {

    if ([[self.viewControllers lastObject]isKindOfClass:[SecondViewController class]]) {
        return UIInterfaceOrientationLandscapeLeft;
    }

    return UIInterfaceOrientationPortrait;
}

这样的好处是,可以控制指定的视图控制器,也不用在指定的视图控制器内再重写上面的方法;
坏处就是,如果需要控制的视图控制器比较多的话,加的判断会多很多;没有方式一灵活。

3)项目根视图控制器是UITabbarController

这种情况比较常见,项目的跟视图控制器是一个UITabBarController,这种情况的实现相对来说要复杂一些,但是,明白了原理,总是有方法来实现的.归根结底,也就是告诉跟视图,我这个界面需要支持旋转了;
由于,在UITabBarController里直接获取到UIViewController比较困难,所以这里我采用通知的形式,来获取相应的设置状态;
首先,设置一个全局的BOOL值,用于接收通知发送的参数:

#import <UIKit/UIKit.h>

@interface BaseTabBar : UITabBarController
{
    BOOL shouldAutorotate;  
}
@end

然后注册一个通知:

//注册旋转屏幕的通知
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(autorotateInterface:) name:@"InterfaceOrientationNotification" object:nil];

实现通知方法:

-(void)autorotateInterface:(NSNotification *)notifition
{
    shouldAutorotate = [notifition.object boolValue];
}

然后在重写的方法里加入判断:

/** 
 * 
 *  @return 是否支持旋转 
 */  
-(BOOL)shouldAutorotate  
{  
    NSLog(@"======%d",shouldAutorotate);  
   return shouldAutorotate;
}  

/** 
 *  适配旋转的类型
 * 
 *  @return 类型 
 */  
-(UIInterfaceOrientationMask)supportedInterfaceOrientations  
{  
    if (!shouldAutorotate) {  
        return UIInterfaceOrientationMaskPortrait;  
    }  
    return UIInterfaceOrientationMaskAllButUpsideDown;  
}  

这里我直接将支持的方向写死了,只是判断了一下是否支持自动旋转屏幕,如果需要将支持的方向传过来,可以修改通知携带的参数;
最后在需要自动转屏的控制器内发送通知:

[[NSNotificationCenter defaultCenter] postNotificationName:@"InterfaceOrientationNotification" object:@"YES"]; 
  • 关于何时发送通知:

例如有这样的需求: A竖屏, 不能自动旋转, B 可横屏可竖屏可自动旋转, 而且在从B回到A的时候, 如果不是竖屏, 强制转为竖屏, 这就用到了下面强制转屏的方法;我们在使用的时候肯定是在B的viewWillAppear方法里发送通知告诉跟视图要自动旋转, 而在离开这个视图时, 就有可能会在viewWillDisappear方法里发送不能自动旋转的通知, 这样是不能实现效果的, 应该放在viewDidDisappear方法里发送;

为严谨起见, 建议在viewDidAppear方法里发送可以自动旋转的通知, 在viewDidDisappear方法里发送不可以自动选择的通知.
这里使用的通知来告知跟视图是否自动旋转, 也可以使用NSUserDefaults来传递这个值, 或者单例都可以。

4)强制旋转屏幕的方法

这个方法可以在进入某个视图时,强制转成你需要的屏幕方向,用的比较多的是在一个竖屏的应用中强制转换某一个界面为横屏(例如播放视频):

这个方法的使用有个前提, 一定是在跟视图的-(BOOL)shouldAutorotate返回值为YES的时候才有效。

// 强制旋转屏幕   
- (void)orientationToPortrait:(UIInterfaceOrientation)orientation {  
    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]; // 前两个参数已被target和selector占用 
    [invocation invoke];  
}  

使用的时候,只需要把你需要旋转的方向传过去即可!
有一点需要注意:从A进入B的时候, 把B强制转换成横屏,返回的时候,需要在A出现的时候再转换为原来的方向,不然会有问题;个人建议可以在B的viewWillAppear调用这个方法,转换屏幕(例如转换为横屏),然后在A的viewWillAppear中转换回来; 如果使用通知来告诉跟视图是否可以自动旋转, 这个方法要在通知发送之后调用.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值