天天看點

【IOS移動開發技術】iOS軟體開發中關于螢幕旋轉處理相關的學習筆記

一、兩種orientation

了解螢幕旋轉首先需要區分兩種orientation

1、device orientation

裝置的實體方向,由類型UIDeviceOrientation表示,目前裝置方向擷取方式:

1 [UIDevice currentDevice].orientation

該屬性的值一般是與目前裝置方向保持一緻的,但須注意以下幾點:

①文檔中對該屬性的注釋:

1 @property(nonatomic,readonly) UIDeviceOrientation orientation;       // return current device orientation.  this will return UIDeviceOrientationUnknown unless device orientation notifications are being generated.

是以更推薦下面這種用法:

more

1

2

3

4

5

6

7

if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) {

        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];

 }

NSLog(@"%d",[UIDevice currentDevice].orientation);

[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];

②系統橫豎屏開關關閉時

如果關閉了系統的橫豎屏切換開關,即系統層級隻允許豎屏時,再通過上述方式擷取到的裝置方向将永遠是UIDeviceOrientationUnknown。可以通過Core Motion中的CMMotionManager來擷取目前裝置方向。

2、interface orientation

界面顯示的方向,由類型UIInterfaceOrientation表示。目前界面顯示方向有以下兩種方式擷取:

1

2

NSLog(@"%d",[UIApplication sharedApplication].statusBarOrientation);

NSLog(@"%d",viewController.interfaceOrientation);

即可以通過系統statusBar的方向或者viewController的方向來擷取目前界面方向。

3、二者差別

通過UIDevice擷取到的裝置方向在手機旋轉時是實時的,通過UIApplication的statusBar或者viewController擷取到的界面方向在下述方法:

1 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:

調用以後才會被更改成最新的值。

二、相關枚舉定義

1、UIDeviceOrientation:

1

2

3

4

5

6

7

8

9

typedef NS_ENUM(NSInteger, UIDeviceOrientation) {

    UIDeviceOrientationUnknown,

    UIDeviceOrientationPortrait,            // Device oriented vertically, home button on the bottom

    UIDeviceOrientationPortraitUpsideDown,  // Device oriented vertically, home button on the top

    UIDeviceOrientationLandscapeLeft,       // Device oriented horizontally, home button on the right

    UIDeviceOrientationLandscapeRight,      // Device oriented horizontally, home button on the left

    UIDeviceOrientationFaceUp,              // Device oriented flat, face up

    UIDeviceOrientationFaceDown             // Device oriented flat, face down

};

2、UIInterfaceOrientation:

1

2

3

4

5

6

7

typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {

    UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,

    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,

    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,

    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,

    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft

};

從宏定義可知,device方向比interface多了兩個定義:UIDeviceOrientationFaceUp和UIDeviceOrientationFaceDown,分别表示手機水準放置,螢幕向上和螢幕向下。

三、相關方法

1、iOS5中控制螢幕旋轉的方法:

1

2

// Applications should use supportedInterfaceOrientations and/or shouldAutorotate..

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation NS_DEPRECATED_IOS(2_0, 6_0);

如果打算支援toInterfaceOrientation對應的方向就傳回YES,否則傳回NO。

2、iOS6中控制螢幕旋轉相關方法:

1

2

3

4

5

// New Autorotation support.

- (BOOL)shouldAutorotate NS_AVAILABLE_IOS(6_0);

- (NSUInteger)supportedInterfaceOrientations NS_AVAILABLE_IOS(6_0);

// Returns interface orientation masks.

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation NS_AVAILABLE_IOS(6_0);

第一個方法決定是否支援多方向旋轉屏,如果傳回NO則後面的兩個方法都不會再被調用,而且隻會支援預設的UIInterfaceOrientationMaskPortrait方向;

第二個方法直接傳回支援的旋轉方向,該方法在iPad上的預設傳回值是UIInterfaceOrientationMaskAll,iPhone上的預設傳回值是UIInterfaceOrientationMaskAllButUpsideDown,詳情見官方Q&A文檔;

第三個方法傳回最優先顯示的螢幕方向,比如同時支援Portrait和Landscape方向,但想優先顯示Landscape方向,那軟體啟動的時候就會先顯示Landscape,在手機切換旋轉方向的時候仍然可以在Portrait和Landscape之間切換;

3、attemptRotationToDeviceOrientation方法

從iOS5開始有了這個新方法:

1

2

3

// call this method when your return value from shouldAutorotateToInterfaceOrientation: changes

// if the current interface orientation does not match the current device orientation, a rotation may occur provided all relevant view controllers now return YES from shouldAutorotateToInterfaceOrientation:

+ (void)attemptRotationToDeviceOrientation NS_AVAILABLE_IOS(5_0);

該方法的使用場景是interface orientation和device orientation不一緻,但希望通過重新指定interface orientation的值,立即實作二者一緻;如果這時隻是更改了支援的interface orientation的值,沒有調用attemptRotationToDeviceOrientation,那麼下次device orientation變化的時候才會實作二者一緻,關鍵點在于能不能立即實作。

舉個例子:

假設目前的interface orientation隻支援Portrait,如果device orientation變成Landscape,那麼interface orientation仍然顯示Portrait;

如果這時我們希望interface orientation也變成和device orientation一緻的Landscape,以iOS6為例,需要先将supportedInterfaceOrientations的傳回值改成Landscape,然後調用attemptRotationToDeviceOrientation方法,系統會重新詢問支援的interface orientation,已達到立即更改目前interface orientation的目的。

四、如何決定interface orientation

1、全局控制

Info.plist檔案中,有一個Supported interface orientations,可以配置整個應用的螢幕方向,此處為全局控制。

2、UIWindow

iOS6的UIApplicationDelegate提供了下述方法,能夠指定 UIWindow 中的界面的螢幕方向:

1 - (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window  NS_AVAILABLE_IOS(6_0);

該方法預設值為Info.plist中配置的Supported interface orientations項的值。

iOS中通常隻有一個window,是以此處的控制也可以視為全局控制。

3、controller

隻有以下兩種情況:

  • 目前controller是window的rootViewController
  • 目前controller是modal模式的

時,orientations相關方法才會起作用(才會被調用),目前controller及其所有的childViewController都在此作用範圍内。

4、最終支援的螢幕方向

前面所述的3種控制規則的交集就是一個controller的最終支援的方向;

如果最終的交集為空,在iOS6以後會抛出UIApplicationInvalidInterfaceOrientationException崩潰異常。

四、強制螢幕旋轉

如果interface和device方向不一樣,想強制将interface旋轉成device的方向,可以通過attemptRotationToDeviceOrientation實作,但是如果想将interface強制旋轉成任一指定方向,該方式就無能為力了。

不過聰明的開發者們總能想到解決方式:

1、私有方法

1 [[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];

但是現在蘋果已經将該方法私有化了,越獄開發的同學可以試試,或者自己想法子騙過蘋果稽核吧。

2、旋轉view的transform

也可以通過旋轉view的transform屬性達到強制旋轉螢幕方向的目的,但個人感覺這不是靠譜的思路,可能會帶來某些詭異的問題。

3、主動觸發orientation機制

要是能主動觸發系統的orientation機制,調用orientation相關方法,使新設定的orientation值起作用就好了。這樣隻要提前設定好想要支援的orientation,然後主動觸發orientation機制,便能實作将interface orientation旋轉至任意方向的目的。

萬能的stackoverflow上提供了一種主動觸發的方式:

在iOS4和iOS6以後:

1

2

3

4

UIViewController *vc = [[UIViewController alloc]init];

[self presentModalViewController:vc animated:NO];

[self dismissModalViewControllerAnimated:NO];

[vc release];

iOS5中:

1

2

3

4

UIWindow *window = [[UIApplication sharedApplication] keyWindow];

UIView *view = [window.subviews objectAtIndex:0];

[view removeFromSuperview];

[window addSubview:view];

這種方式會觸發UIKit重新調用controller的orientation相關方法,以達到在device方向不變的情況下改變interface方向的目的。

雖然不優雅,但卻能解決問題,湊合吧。。

PS:

話說iOS8中的螢幕旋轉相關方法又變化了,表示适配起來很蛋疼。。。

五、參考文檔

  • Why won’t my UIViewController rotate with the device?;
  • How to force a UIViewController to Portait orientation in iOS 6
  • IOS Orientation, 想怎麼轉就怎麼轉
  • iOS 螢幕方向那點事兒

原文位址:http://foggry.com/blog/2014/08/08/ping-mu-xuan-zhuan-xue-xi-bi-ji/

 【關于我們】

才淇(微信公衆号:caiqicehua),專注于國内各大網際網路公司社會招聘内推。每天更新最新網際網路名企(包括但不限于今日頭條、網易遊戲、BAT、網易網際網路、小米、京東、樂視、攜程等名企)内推資訊,有技術崗、有産品崗、有營運崗、有設計崗、有互動崗、有銷售崗,更有其他N多相關崗位!更多内推資訊請掃描以下二維碼關注查閱。

【IOS移動開發技術】iOS軟體開發中關于螢幕旋轉處理相關的學習筆記