UIDeviceOrientation與UIinterfaceOrientation以及屏幕旋轉的方式

UIDeviceOrientation與UIinterfaceOrientation以及屏幕旋轉的方式

在日常開發過程中,經常會遇到轉屏的需求,最近遇到一個轉屏相關的bug,順帶著總結下iOS端實現轉屏需要做的事情。

什么是設備方向,什么是視圖方向

首先需要明確兩個概念,設備方向(UIDeviceOrientation)和視圖方向(UIInterfaceOrientation)

UIDeviceOrientation:

不受鎖定屏幕方向的影響,通過

[UIDevice currentDevice].orientation

客觀反映出當前設備所處的方向,與視野中正在展示視圖的方向無關,該屬性只讀,無法修改

該屬性的變化可以通過監聽UIDeviceOrientationDidChangeNotification來實時獲得設備方向的變化

UIInterfaceOrientation:

多數的旋轉都需要通過旋轉controller來實現。controller的方向也就是我上面提到的視圖方向,使用該枚舉UIInterfaceOrientation來表達。

如果想獲得當前的視圖方向,可以通過以下代碼獲得

[UIApplication sharedApplication].statusBarOrientation

視圖方向的改變一定是由于設備方向發生了變化,設備方向的改變也只能是由于物理改變造成的。但是有一個例外,會在后面強制轉屏部分詳細說明。

如何讓Controller隨設備方向旋轉

1. 配置App支持的視圖方向

首先需要配置下當前App能支持的視圖方向,所有使用的controller所能支持的視圖方向是該配置的子集;

可以通過以下兩種方法進行配置:

方法1,在Xcode選項中配置:

在Xcode->工程->General->Deployment Info中對Device Orientation進行配置,注意這里的Device Orientation不是上面所說的設備方向

方法2,在AppDelegate中通過代碼配置:

在AppDelegate中實現如下方法:

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

return UIInterfaceOrientationMaskAll;

}

如果同時使用了兩種方式,那第二種使用代碼的方式會覆蓋第一種在Xcode中配置的方式

2. 配置Controller支持的方向

代碼很簡單,表示當前Controller是否支持旋轉,支持的方向都有什么,如下:

//是否支持轉屏

- (BOOL)shouldAutorotate

{

return YES;

}

//在支持轉屏的前提下,返回具體支持的方向

- (UIInterfaceOrientationMask)supportedInterfaceOrientations

{

return UIInterfaceOrientationMaskAll;

}

但是這段代碼具體要寫在哪個viewController中才有效果呢?

一個viewController是否可以旋轉并不是由他自己決定的,而是由rootViewController或者最上層被present上來的viewController來決定的。

每個App都有一個rootViewController,如果沒有通過present的方式推入controller,那么rootViewController支持的方向就決定了視圖方向;

如果有通過present的方式推入controller,那么最近的一次present操作對應的controller就決定了視圖方向;

以上的代碼需要放在上面說到的決定了視圖方向的controller中

還有一個回調方法,決定了viewController出現時的視圖方向,該回調方法只對present方式進入界面的viewController有效果

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation

{

return UIInterfaceOrientationPortrait;

}

當然還有一點最重要的,記得要在下拉框中打開設備的轉屏鎖,要不然怎么轉ViewController都不會跟著轉的

關于強制轉屏

在上文中提過,設備方向改變一定是由于物理改變造成的,其實這并不絕對

蘋果的API曾經是允許修改[UIDevice currentDevice].orientation的,后來這個接口被干掉作為私有API了

但是我們還是可以通過一些其他方式繞過蘋果的限制來設置[UIDevice currentDevice].orientation,設備方向一旦改變,再符合上述視圖方向改變的條件,視圖方向就會被旋轉,從而達到強制轉屏的效果

在實際開發過程中也會遇到類似的場景,比如播放器的全屏操作,就是一次強制的視圖方向改變

代碼如下:

NSNumber *orientationTarget = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];

[[UIDevice currentDevice] setValue:orientationTarget forKey:@"orientation"];

關于UINavigationController的旋轉

其實道理是一樣的,只不過如果present上來一個navigationController的時候,你需要想辦法讓這個navigationController去實現上述的shouldAutoRotate和supportedInterfaceOrientations方法,可以通過類別或繼承重寫的方式,這里也就不詳述了。




iOS的橫屏(Landscape)與豎屏(Portrait)InterfaceOrientation

0. 應用級別的配置

大家(特指有iOS開發經驗的人)應該都知道Xcode Project的工程配置General頁簽中有那么四個圖(或者4個checkbox),標識對四種interfaceOrientation的支持。分別為Portrait、PortraitUpsideDown、LandscapeLeft和LandscapeRight。

對應的,在Xcode Project工程配置的Info頁,實際上就是Info.plist中,有對4種Orientation的記錄項。

這兩者是一樣的。

1. Window級別的控制

在iOS6.0之后,UIApplicationDelegate中多了一個方法聲明:

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

就是對于特定的application和特定的window,我們需要支持哪些interfaceOrientation,這是可以通過實現這個方法定制的。

返回值是一個無符號整數,實際上是可以使用定義好的枚舉值:

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),

};

對于UIApplicationDelegate的這個方法聲明,大多數情況下application就是當前的application,而window通常也只有一個。所以基本上通過window對橫屏豎屏interfaceOrientation的控制相當于全局的。

2. Controller層面的控制

老版本的iOS有這樣一個方法:

1- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientationNS_DEPRECATED_IOS(2_0, 6_0);

即定制是否可以旋轉到特定的interfaceOrientation。

而在iOS6之后,推出了2個新的方法來完成這個任務:

1

2

- (BOOL)shouldAutorotateNS_AVAILABLE_IOS(6_0);

- (NSUInteger)supportedInterfaceOrientationsNS_AVAILABLE_IOS(6_0);

可以看得出來,兩個和在一起就是原來任務的完成過程。其中,大概的判斷方式是,先執行前者,判斷是否可以旋轉,如果為YES,則根據是否支持特定的interfaceOrientation再做決斷。

3. 使得特定ViewController堅持特定的interfaceOrientation

iOS6之后還提供了這樣一個方法,可以讓你的Controller倔強第堅持某個特定的interfaceOrientation:

1- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentationNS_AVAILABLE_IOS(6_0);

這就叫堅持,呵呵!

當然,使用這個方法是有前提的,就是當前ViewController是通過全屏的Presentation方式展現出來的。

這里使用的是另外一套枚舉量,可以去UIApplication.h中查看定義。

4. 當前屏幕方向interfaceOrientation的獲取

有3種方式可以獲取到“當前interfaceOrientation”:

controller.interfaceOrientation,獲取特定controller的方向

[[UIApplication sharedApplication] statusBarOrientation] 獲取狀態條相關的方向

[[UIDevice currentDevice] orientation] 獲取當前設備的方向

具體區別,可參見StackOverflow的問答:

http://stackoverflow.com/questions/7968451/different-ways-of-getting-current-interface-orientation

5. 容器Controller的支持

上面把interfaceOrientation方向的獲取和支持配置都說了,看起來沒什么問題了。有沒有什么特殊情況?

當你使用TabbarController和NavigationController按照如上做法使用的時候就會有些頭疼。

辦法不是沒有,比較通俗的一種就是——繼承實現。

6. UIView的transform屬性強制旋轉.

最后一個方法是設置UIView的transform屬性來強制旋轉.

見下代碼:

//設置statusBar[[UIApplication sharedApplication] setStatusBarOrientation:orientation];//計算旋轉角度float arch;if (orientation == UIInterfaceOrientationLandscapeLeft)? ? arch = -M_PI_2;else if (orientation == UIInterfaceOrientationLandscapeRight)? ? arch = M_PI_2;else? ? arch = 0;//對navigationController.view 進行強制旋轉self.navigationController.view.transform = CGAffineTransformMakeRotation(arch);self.navigationController.view.bounds = UIInterfaceOrientationIsLandscape(orientation) ? CGRectMake(0, 0, SCREEN_HEIGHT, SCREEN_WIDTH) : initialBounds;

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • /* UIViewController is a generic controller base class th...
    DanDanC閱讀 1,861評論 0 2
  • 1.監聽屏幕旋轉方向 在處理iOS橫豎屏時,經常會和UIDeviceOrientation、UIInterface...
    彬至睢陽閱讀 2,555評論 1 6
  • [這是第11篇] 導語: iOS App中大多數頁面是只展示豎屏下的效果,但是少部分頁面需要支持橫豎屏。本文分別介...
    南華coder閱讀 14,537評論 18 93
  • iOS屏幕旋轉學習筆記iOS開發中使用屏幕旋轉功能的相關方法 1、基本知識點解讀 了解屏幕旋轉首先需要區分兩種 o...
    Laughingg閱讀 13,566評論 13 39
  • 由于講師訓的第一次機會在我坐月子的時間,那時候由于剛生完孩子不到一星期的我,并不敢任性的看手機,然而也沒少操心。由...
    cindyzhao閱讀 255評論 0 0