此博文主要針對IOS應用, 是屏幕旋轉相關問題的一個總結. 主要內容有:
- IOS5,6,7不同版的適配.
- 強制旋轉和自動旋轉.
- 博客: http://www.cnblogs.com/jhzhu
- 郵箱: jhzhuustc@gmail.com
- 作者: 知明所以
- 時間: 2013-12-12
改變Orientation的三種途徑
這里, 咱們主要理清一下: 到底有哪些設置可以改變屏幕旋轉特性. 這樣:
- 出現任何問題我們都可以從這幾個途徑中發現原因.
- 靈活應付產品經理的各種需求.
首先我們得知道:
- 當手機的重力感應打開的時候, 如果用戶旋轉手機, 系統會拋發
UIDeviceOrientationDidChangeNotification
事件. - 您可以分別設置
Application
和UIViewcontroller
支持的旋轉方向.Application
的設置會影響整個App,UIViewcontroller
的設置僅僅會影響一個viewController
(IOS5和IOS6有所不同,下面會詳細解釋). - 當
UIKit
收到UIDeviceOrientationDidChangeNotification
事件的時候, 會根據Application
和UIViewcontroller
的設置, 如果雙方都支持此方向, 則會自動屏幕旋轉到這個方向. 更code的表達就是, 會對兩個設置求與,得到可以支持的方向. 如果求與之后,沒有任何可支持的方向, 則會拋發UIApplicationInvalidInterfaceOrientationException
異常.
Info.plist設置
在App的Info.plist里設置:
key | xcode name | Summary | avilable value |
---|---|---|---|
UIInterfaceOrientation | initial interface orientation | Specifies the initial orientation of the app’s user interface. | UIInterfaceOrientationPortrait, UIInterfaceOrientationPortraitUpsideDown, UIInterfaceOrientationLandscapeLeft, UIInterfaceOrientationLandscapeRight |
UISupportedInterfaceOrientations | Supported interface orientations | Specifies the orientations that the app supports. | UIInterfaceOrientationPortrait, UIInterfaceOrientationPortraitUpsideDown, UIInterfaceOrientationLandscapeLeft, UIInterfaceOrientationLandscapeRight |
在Info.plist中設置之后,這個app里所有的viewController
支持的自動旋轉方向都只能是app支持的方向的子集.
UIViewController
IOS6 and above
supportedInterfaceOrientations
在IOS6及以上的版本中, 增添了方法UIViewController.supportedInterfaceOrientations
. 此方法返回當前viewController
支持的方向. 但是, 只有兩種情況下此方法才會生效:
- 當前
viewController
是window
的rootViewController
. - 當前
viewController
是modal
模式的. 即, 此viewController
是被調用presentModalViewController
而顯示出來的.
在以上兩種情況中,UIViewController.supportedInterfaceOrientations
方法會作用於當前viewController
和所有childViewController
. 以上兩種情況之外, UIKit
並不會理會你的supportedInterfaceOrientations
方法.
舉個栗子:
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;
}
如果某個viewController
實現了以上方法. 則, 此viewController
就支持豎方向和左旋轉方向. 此viewController
的所有childViewController
也同時支持這兩個方向, 不多不少.
preferredInterfaceOrientationForPresentation
此方法也屬於UIViewController
. 影響當前viewController
的初始顯示方向. 此方法也僅有在當前viewController
是rootViewController
或者是modal
模式時才生效.
shouldAutorotate
此方法,用於設置當前viewController
是否支持自動旋轉. 如果,你需要viewController
暫停自動旋轉一小會兒. 那么可以通過這個方法來實現.同樣的, 此方法也僅有在當前viewController
是rootViewController
或者是modal
模式時才生效.
IOS5 and before
在IOS5和以前的版本中, 每個viewController
都可以指定自己可自動旋轉的方向.(這樣不是挺好么?蘋果那幫工程師為啥要搞成這樣...).
每當UIkit
收到UIDeviceOrientationDidChangeNotification
消息的時候, 就會用以下方法詢問當前顯示的viewController
支不支持此方向:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation
{
if ((orientation == UIInterfaceOrientationPortrait) ||
(orientation == UIInterfaceOrientationLandscapeLeft))
return YES;
return NO;
}
特別要注意的是:你必須至少要對一個方向返回YES
.(為難系統總不會有啥好事兒,你懂得).
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;
需要注意的是:
- 當然我們可以對當前
viewController
進行旋轉, 對任何view
旋轉都可以.但是, 你會發現navigationBar
還橫在那里. 所以, 我們最好對一個占滿全屏的view
進行旋轉. 在這里我們旋轉的對象是self.navigationController.view
, 當然self.window
也可以, help yourself~ - 我們需要顯式的設置
bounds
.UIKit
並不知道你偷偷摸摸干了這些事情, 所以沒法幫你自動設置.
如何應付產品經理的需求
有了以上三把武器, 我想基本可以應付BT產品經理所有的需求了. 但是這里還有一些小技巧.
直接鎖死
(略)
隨系統旋轉
IOS5及之前
對於IOS5及之前的版本, 只要在對每個viewController
重寫shouldAutorotateToInterfaceOrientation
方法, 即可方便的控制每個viewController
的方向.
IOS6及以后
對於IOS6及以后的版本, 如果想方便的單獨控制每個viewController
的方向. 則可以使用這樣:
-
對於非
modal
模式的viewController
:- 如果不是
rootViewController
,則重寫supportedInterfaceOrientations
,preferredInterfaceOrientationForPresentation
以及shouldAutorotate
方法, 按照當前viewController
的需要返回響應的值. - 如果是
rootViewController
,則如下重寫方法:
- 如果不是
-(NSUInteger)supportedInterfaceOrientations
{
return self.topMostViewController.supportedInterfaceOrientations;
}
-(BOOL)shouldAutorotate
{
return [self.topMostViewController shouldAutorotate];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [self.topMostViewController preferredInterfaceOrientationForPresentation];
}
-(UIViewController*)topMostViewController
{
//找到當前正在顯示的viewController並返回.
}
顯而易見, 我們巧妙的繞開了UIKit
只調用rootViewController
的方法的規則. 把決定權交給了當前正在顯示的viewController
.
- 對於
modal
模式的viewController
. 則按照需要重寫supportedInterfaceOrientations
,preferredInterfaceOrientationForPresentation
以及shouldAutorotate
方法即可.
強制旋轉
有時候, 需要不隨系統旋轉, 而是強制旋轉到某一個角度. 最典型的場景就是視頻播放器, 當點擊了全屏按鈕的時候, 需要橫過來顯示.
- 對於IOS5及以前的版本, 可以用下面的方法:
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
SEL selector = NSSelectorFromString(@"setOrientation:");
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:[UIDevice currentDevice]];
int val = UIInterfaceOrientationLandscapeRight;
[invocation setArgument:&val atIndex:2];
[invocation invoke];
}
- 對於IOS6及以后的版本.
UIDevice.setOrientation
從隱藏變為移除.只能通過設置UIView.transform
的方法來實現.
參考資料