【精】iOS6 及其以上版本號本身主動旋轉、手動強制旋轉方案及佈局適配

一、佈局適配方式

本文不討論哪一種佈局適配方式最好。此處使用的是 Masonry 純代碼佈局適配。

(Masonry 底層就是 AutoLayout 的 NSLayoutConstraint)
git

二、iOS 方向枚舉類

// 三維設備方向
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
};

// 二維界面方向
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
    UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
};

// iOS6 之後引入組合方式
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),
};
獲取設備方向:[[UIDevice currentDevice] orientation]
獲取界面方向:[[UIApplication sharedApplication] statusBarOrientation]

三、iOS6 及其以上版本號頁面旋轉設置方法

// 返回是否支持屏幕旋轉
- (BOOL)shouldAutorotate
{
    return YES;  
}

// 返回支持的旋轉方向
- (NSUInteger)supportedInterfaceOrientations  
{  
    return UIInterfaceOrientationMaskAll;  
}

// 返回優先顯示的屏幕方向,假設不設置,默認與進入前一個頁面保持一致(注意該方法僅僅對 ModalViewController 有效)
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{  
    return UIInterfaceOrientationLandscapeLeft;  
}

四、影響界面旋轉特性的層級因素

(1)針對全局
Info.plist 文件裏 Supported interface orientations 支持的方向。
(2)針對 Window
AppDelegate 中 supportedInterfaceOrientationsForWindow 支持的方向。
(3)針對單個頁面
假設是 ChildrenViewController,則受限於其 RootViewController 中 shouldAutorotate 和 supportedInterfaceOrientations 支持的方向。(RootViewController 指 self.window.rootViewController 設置的那個 ViewController)
假設是 ModalViewController,則受限於其自身 shouldAutorotate 和 supportedInterfaceOrientations 支持的方向。(假設 ModalViewController 是通過包裝的還有一個 RootViewController,則與上述 ChildrenViewController 原理相似)
注意:上述三個層級因素終於的交集即爲子視圖控制器支持的旋轉方向,假設交集爲空。則會拋 UIApplicationInvalidInterfaceOrientationException 異常。

五、屏幕旋起色制流程

(1)加速計檢測到方向變化。發出 UIDeviceOrientationDidChangeNotification 通知。
(2)程序接收到通知,經過 AppDelegate 知會當前程序的 Window。
(3)Window 通知 RootViewController。依據下面設置決定是否旋轉。

  • Info.plist 中 Supported interface orientations 是否支持該方向
  • AppDelegate 中 supportedInterfaceOrientationsForWindow 中是否支持該方向
  • RootViewController 中 shouldAutorotate 是否爲 YES
  • RootViewController 中 supportedInterfaceOrientations 是否支持該方向
(4)RootViewController 通知其 ChildrenViewController,同步旋轉操做。


普通狀況下 ChildrenViewController 不單獨設置。與 RootViewController 保持一致。假設特殊場景需要單獨設置,可以經過在 RootViewController 中下放權限。如:NavigationController 可以經過 self.topViewController 下放權限;TabBarController 可以經過 self.selectedViewController 下放權限。但是要注意,即便下放了權限,ChildrenViewController 仍是必須遵照 Info.plist 和 AppDelegate 中的設置。
(5)假設存在彈出的 ModalViewController,則不受限於步驟4中的 RootViewController,僅僅依據 Info.plist、AppDelegate 及其自身所支持的旋轉設置決定是否旋轉。假設 ModalViewController 是通過包裝的還有一個 RootViewController。則與步驟4原理相似。
github

六、產品開發中的應對策略。

(1)應用僅僅需要支持單一方向。

在 Info.plist 中鎖死指定方向。限制屏幕旋轉。
(2)應用統一支持多個方向本身主動旋轉。在 Info.plist 中設置應用支持的方向,每個頁面進行對應的本身主動旋轉佈局適配。ide


(3)應用不一樣頁面支持的方向不一致。在 Info.plist 中設置所有頁面支持的方向的並集。在 RootViewController 中將權限下放,由頁面但與控制本身的旋轉設置。佈局


七、實際場景應用(有演示樣例 Demo)

注意:本文不考慮 iOS6 下面版本號的兼容性。所下面述 demo 僅僅適配 iOS6 及其以上版本號(僅僅在 iOS七、iOS8 測試過)。下述場景處理方案中,iPad 默認支持四個方向,iPhone 默認支持 UIInterfaceOrientationMaskPortraitUpsideDown 三個方向。
(1)應用場景1:應用支持單一方向,限制旋轉。(iPhone 中通常此方式用得比較多)
思路:在 Info.plist 中鎖死指定方向,其它旋轉設置均不用配置。適配方式比較多,也比較easy,只是建議純代碼的話仍是經過 Masonry 進行佈局適配。
演示樣例:LimitPortraitDemo
(2)應用場景2:應用統一支持多方向本身主動旋轉。(iPad 中通常此方式用得比較多)
思路:在 Info.plist 中設置應用支持的旋轉方向就能夠。其它旋轉設置均不用配置。佈局要分別適配橫屏與豎屏,純代碼的話建議經過 Masonry 進行佈局適配。
演示樣例:AnyRotationDemo
(3)應用場景3:應用支持單一方向。但是個別頁面支持本身主動旋轉。(通常不建議使用,除非特定場景,如視頻播放器頁面,本身主動旋轉後橫屏觀看效果更好)
思路:在 Info.plist 中設置應用支持的所有旋轉方向。在 RootViewController 中經過 shouldAutorotate 和 supportedInterfaceOrientations 鎖死指定方向。而後在 ModalViewController 中經過 shouldAutorotate 和 supportedInterfaceOrientations 設置多個旋轉方向。並進行對應的佈局適配。

適配方式純代碼的話相同建議 Masonry。
演示樣例:SingleRotationDemo
(4)應用場景4:應用支持單一方向,但是個別頁面支持手動控制旋轉。不支持本身主動旋轉。(通常不建議使用,除非特定場景。如視頻播放器頁面限制本身主動旋轉,點擊全屏button後橫屏觀看)
思路:有兩種強制方式旋轉方式,一種是帶導航欄的,一種是不帶導航欄的。具體實現思路演示樣例中有具體描寫敘述。
演示樣例:ForceRotationDemo
post

八、相關注意事項

  • 通常都不建議在程序裏面直接調用 UIDeviceOrientation 的方向,而是用 UIInterfaceOrientation。

  • 獲取屏幕方法,不要使用[[UIDevice curentDevice] orientation],建議使用[[UIApplication sharedApplication] statusBarOrientation]。
  • 假設 shouldAutorotate 返回 YES 的話,設置 setStatusBarOrientation 是不管用的。
  • 假設 shouldAutorotate 返回 NO 的話,[[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)] 方法是不管用的。
  • 儘可能不要使用強制旋轉的方式。經過 present 出 ModalViewController 方式。單獨控制每個試圖控制器的旋轉設置。

    蘋果官方也是支持這樣的方式。spa

相關文章
相關標籤/搜索