1. 程式人生 > >程式碼處理 iOS 的橫豎屏旋轉

程式碼處理 iOS 的橫豎屏旋轉

一、監聽螢幕旋轉方向

在處理iOS橫豎屏時,經常會和UIDeviceOrientation、UIInterfaceOrientation和UIInterfaceOrientationMask這三個列舉型別打交道,它們從不同角度描述了螢幕旋轉方向。

1UIDeviceOrientation:裝置方向

iOS的裝置方向是通過iOS的加速計來獲取的。

1)iOS定義了以下七種裝置方向

typedefNS_ENUM(NSInteger, UIDeviceOrientation) {

    UIDeviceOrientationUnknown,                 // 未知方向,可能是裝置(螢幕)斜置

    UIDeviceOrientationPortrait,                // 裝置(螢幕)直立

    UIDeviceOrientationPortraitUpsideDown,      // 裝置(螢幕)直立,上下顛倒

    UIDeviceOrientationLandscapeLeft,           // 裝置(螢幕)向左橫置

    UIDeviceOrientationLandscapeRight,          // 裝置(螢幕)向右橫置

    UIDeviceOrientationFaceUp,                  // 裝置(螢幕)朝上平躺

    UIDeviceOrientationFaceDown                 // 裝置(螢幕)朝下平躺

};

說明:UIDeviceOrientation參考home鍵方向,如:home方向在右,裝置(螢幕)方向向左(UIDeviceOrientationLandscapeLeft)

2)讀取裝置方向

UIDevice單例代表當前的裝置。從這個單例中可以獲得的資訊裝置,如裝置方向orientation。

UIDeviceOrientation deviceOrientation = [UIDevicecurrentDevice].orientation;

3)監聽、處理和移除 裝置方向改變的通知

當裝置方向變化時候,發出UIDeviceOrientationDidChangeNotification通知;註冊監聽該通知,可以針對不同的裝置方向處理檢視展示。

//開啟和監聽 裝置旋轉的通知(不開啟的話,裝置方向一直是UIInterfaceOrientationUnknown)

if (![UIDevicecurrentDevice].generatesDeviceOrientationNotifications) {

        [[UIDevicecurrentDevice] beginGeneratingDeviceOrientationNotifications];

    }

    [[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(handleDeviceOrientationChange:)

name:UIDeviceOrientationDidChangeNotificationobject:nil];

//裝置方向改變的處理

- (void)handleDeviceOrientationChange:(NSNotification *)notification{

UIDeviceOrientation deviceOrientation = [UIDevicecurrentDevice].orientation;

switch (deviceOrientation) {

caseUIDeviceOrientationFaceUp:

NSLog(@"螢幕朝上平躺");

break;

caseUIDeviceOrientationFaceDown:

NSLog(@"螢幕朝下平躺");

break;

caseUIDeviceOrientationUnknown:

NSLog(@"未知方向");

break;

caseUIDeviceOrientationLandscapeLeft:

NSLog(@"螢幕向左橫置");

break;

caseUIDeviceOrientationLandscapeRight:

NSLog(@"螢幕向右橫置");

break;

caseUIDeviceOrientationPortrait:

NSLog(@"螢幕直立");

break;

caseUIDeviceOrientationPortraitUpsideDown:

NSLog(@"螢幕直立,上下顛倒");

break;

default:

NSLog(@"無法辨識");

break;

    }

}

//最後在dealloc中移除通知 和結束裝置旋轉的通知

- (void)dealloc{

//...

    [[NSNotificationCenterdefaultCenter]removeObserver:self];

    [[UIDevicecurrentDevice]endGeneratingDeviceOrientationNotifications];

}

說明:手機鎖定豎屏後,UIDeviceOrientationDidChangeNotification通知就失效了。

2UIInterfaceOrientation:介面方向

介面方向是反應iOS中介面的方向,它和Home按鈕的方向是一致的。

1)iOS定義了以下五種介面方向

typedefNS_ENUM(NSInteger, UIInterfaceOrientation) {

    UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,      //未知方向

    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,              //介面直立

    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown, //介面直立,上下顛倒

    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,  //介面朝左

    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft   //介面朝右

} __TVOS_PROHIBITED;

說明:從定義可知,介面方向和設別方向有對應關係,如介面的豎直方向就是 裝置的豎直方向:UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown

2)讀取介面方向

UIInterfaceOrientation和狀態列有關,通過UIApplication的單例呼叫statusBarOrientation來獲取

UIInterfaceOrientation interfaceOrientation = [[UIApplicationsharedApplication] statusBarOrientation];

3)監聽、處理和移除 介面方向改變的通知

當介面方向變化時候,先後發出UIApplicationWillChangeStatusBarOrientationNotification和UIApplicationDidChangeStatusBarOrientationNotification通知;註冊監聽這兩個通知,可以針對不同的介面方向處理檢視展示。

//以監聽UIApplicationDidChangeStatusBarOrientationNotification通知為例

[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(handleStatusBarOrientationChange:)

name:UIApplicationDidChangeStatusBarOrientationNotificationobject:nil];

//介面方向改變的處理

- (void)handleStatusBarOrientationChange: (NSNotification *)notification{

UIInterfaceOrientation interfaceOrientation = [[UIApplicationsharedApplication] statusBarOrientation];

switch (interfaceOrientation) {

caseUIInterfaceOrientationUnknown:

NSLog(@"未知方向");

break;

caseUIInterfaceOrientationPortrait:

NSLog(@"介面直立");

break;

caseUIInterfaceOrientationPortraitUpsideDown:

NSLog(@"介面直立,上下顛倒");

break;

caseUIInterfaceOrientationLandscapeLeft:

NSLog(@"介面朝左");

break;

caseUIInterfaceOrientationLandscapeRight:

NSLog(@"介面朝右");

break;

default:

break;

    }

}

//最後在dealloc中移除通知

- (void)dealloc{

//...

    [[NSNotificationCenterdefaultCenter]removeObserver:self];

    [[UIDevicecurrentDevice]endGeneratingDeviceOrientationNotifications];

}

說明:手機鎖定豎屏後,UIApplicationWillChangeStatusBarOrientationNotificationUIApplicationDidChangeStatusBarOrientationNotification通知也失效了。

3UIInterfaceOrientationMask

UIInterfaceOrientationMask是為了整合多種UIInterfaceOrientation而定義的型別,和ViewController相關,一共有7種

1)iOS中的UIInterfaceOrientationMask定義

typedefNS_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{

returnUIInterfaceOrientationMaskPortrait;

}

總結:UIDeviceOrientation(裝置方向)UIInterfaceOrientation(螢幕方向)是兩個不同的概念。前者代表了裝置的一種狀態,而後者是螢幕為了應對不同的裝置狀態,做出的使用者介面上的響應。在iOS裝置旋轉時,由UIKit接收到旋轉事件,然後通過AppDelegate通知當前程式的UIWindow物件,UIWindow物件通知它的rootViewController,如果該rootViewController支援旋轉後的螢幕方向,完成旋轉,否則不旋轉;彈出的ViewController也是如此處理。

二、檢視控制器中旋轉方向的設定

0、關於禁止橫屏的操作(不建議)

比較常規的方法有兩種。

方法1在專案的General–>Deployment Info–>Device Orientation中,只勾選Portrait(豎屏)

da259f6f557f534162c0e902a54223e2.png

勾選Portrait.png

方法2Device Orientation預設設定,在Appdelegate中實現supportedInterfaceOrientationsForWindow:只返回UIInterfaceOrientationMaskPortraitt(豎屏)

-  (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window  {

returnUIInterfaceOrientationMaskPortrait;

}

說明:極少的APP中所有介面都是豎屏的,因為總會有介面需要支援橫屏,如視訊播放頁。所以不建議設定禁止APP頁面橫屏

下面介紹如何讓專案中的 檢視控制器中旋轉方向的設定

1APP支援多個方向

599b685031c08cd5546068faef799835.png

APP支援多個方向.png

說明:如此,APP支援橫屏和豎屏了,但是具體檢視控制器支援的頁面方向還需要進一步處理。由於不支援豎屏顛倒(Upside Down),即使裝置上下顛倒,通過API也不會獲得裝置、螢幕上下顛倒方向的。

2、支援ViewController螢幕方向設定

1)關鍵函式

檢視控制器支援的介面方向主要由以下三個函式控制

//是否自動旋轉,返回YES可以自動旋轉,返回NO禁止旋轉

- (BOOL)shouldAutorotateNS_AVAILABLE_IOS(6_0)__TVOS_PROHIBITED;

//返回支援的方向

- (UIInterfaceOrientationMask)supportedInterfaceOrientationsNS_AVAILABLE_IOS(6_0)__TVOS_PROHIBITED;

//由模態推出的檢視控制器 優先支援的螢幕方向

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentationNS_AVAILABLE_IOS(6_0)__TVOS_PROHIBITED;

2) QSBaseViewController設定

//QSBaseViewController.h

@interface QSBaseController : UIViewController

@end

//QSBaseViewController.m

@implementation QSBaseController

//#pragma mark - 控制螢幕旋轉方法

//是否自動旋轉,返回YES可以自動旋轉,返回NO禁止旋轉

- (BOOL)shouldAutorotate{

returnNO;

}

//返回支援的方向

- (UIInterfaceOrientationMask)supportedInterfaceOrientations{

returnUIInterfaceOrientationMaskPortrait;

}

//由模態推出的檢視控制器 優先支援的螢幕方向

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{

returnUIInterfaceOrientationPortrait;

}

@end

說明1:QSBaseViewController預設不支援旋轉,只支援 介面豎直方向,專案中的Controller都繼承自QSBaseViewController,可以通過重寫這三個方法來讓Controller支援除豎屏之外的方向或旋轉。

3) 在QSNavigationController設定

目標:通過QSNavigationController來push檢視控制器時,把支援螢幕旋轉的設定交給最新push進來([self.viewControllers lastObject])的viewController來設定。

//QSNavigationController.h

@interface QSNavigationController :UINavigationController

@end

//QSNavigationController.m

@implementation QSNavigationController

#pragma mark - 控制螢幕旋轉方法

- (BOOL)shouldAutorotate{

return [[self.viewControllerslastObject]shouldAutorotate];

}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations{

return [[self.viewControllerslastObject]supportedInterfaceOrientations];

}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{

return [[self.viewControllerslastObject] preferredInterfaceOrientationForPresentation];

}

@end

4) 在QSTabBarController設定

目標:TabBarController通常作為整個程式的rootViewController,UITabBar上面顯示的每一個Tab都對應著一個ViewController;每點選一個Tab,出現的ViewController(self.selectedViewController)對螢幕旋轉和支援方向的設定 交給其自身去控制。

//QSTabBarController.h

@interface QSTabBarController :UITabBarController

@end

//QSTabBarController.m

@implementation QSTabBarController

#pragma mark - 控制螢幕旋轉方法

- (BOOL)shouldAutorotate{

return [self.selectedViewControllershouldAutorotate];

}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations{

return [self.selectedViewControllersupportedInterfaceOrientations];

}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{

return [self.selectedViewControllerpreferredInterfaceOrientationForPresentation];

}

@end

三、螢幕旋轉方向下的檢視處理

1、螢幕旋轉時,建議監聽UIApplicationDidChangeStatusBarOrientationNotification

原因1supportedInterfaceOrientations方法中最終返回的是多個介面方向

原因2(最重要的原因):我們真正要處理的是頁面方向發生旋轉UI的變化。而在裝置的物理方向發生旋轉的時候,如果此時當前控制器的頁面並沒有旋轉,我們這時改變UI佈局,可能就發生問題了。

2、螢幕的寬高處理

1)在iOS 8之後,當螢幕旋轉的時候,[[UIScreenmainScreen]bounds]也發生了改變。如橫屏時候的螢幕寬度 其實是豎屏的時候螢幕的高度。

2)我們處理檢視佈局時候,如果使用到螢幕的寬高,不要直接使用SCREEN_HEIGHTSCREEN_WIDTH,而使用SCREEN_MINSCREEN_MAX

#define SCREEN_HEIGHT CGRectGetHeight([[UIScreen mainScreen] bounds])

#define SCREEN_WIDTH  CGRectGetWidth([[UIScreen mainScreen] bounds])

#define SCREEN_MIN MIN(SCREEN_HEIGHT,SCREEN_WIDTH)

#define SCREEN_MAX MAX(SCREEN_HEIGHT,SCREEN_WIDTH)

說明:豎屏時候,寬是SCREEN_MIN,高是SCREEN_MAX;橫屏時候,寬是SCREEN_MAX,高是SCREEN_MIN

3、螢幕旋轉下處理Demo

//監聽UIApplicationDidChangeStatusBarOrientationNotification的處理

- (void)handleStatusBarOrientationChange: (NSNotification *)notification{

UIInterfaceOrientation interfaceOrientation = [[UIApplicationsharedApplication] statusBarOrientation];

BOOL isLandscape =NO;

switch (interfaceOrientation) {

caseUIInterfaceOrientationUnknown:

NSLog(@"未知方向");

break;

caseUIInterfaceOrientationPortrait:

caseUIInterfaceOrientationPortraitUpsideDown:

            isLandscape = NO;

break;

caseUIInterfaceOrientationLandscapeLeft:

caseUIInterfaceOrientationLandscapeRight:

            isLandscape = YES;

break;

default:

break;

    }

if (isLandscape) {

self.tableView.frame = CGRectMake(0,0, SCREEN_MAX, SCREEN_MIN -44);

    }else{

self.tableView.frame = CGRectMake(0,0, SCREEN_MIN, SCREEN_MAX -64);

    }

    [self.tableView reloadData];

}

說明:當然也可以選擇使用Masonry這樣優秀的AutoLayout佈局第三方庫來處理,storyBoard來佈局次之。

4、螢幕旋轉下處理Demo效果圖

114b807bfbf5a0f57c43f14e7fb7f717.png

豎屏下效果.png

00e1c6892a5f5f0bce9e88278f4520c7.png

橫屏下效果.png