iOS 屏幕旋轉(zhuǎn)問題總結(jié)

1、UIDeviceOrientation 設(shè)備的物理方向

  • 簡(jiǎn)介
    UIDeviceOrientation即我們手持的移動(dòng)設(shè)備的Orientation,是一個(gè)三圍空間磅废,故有六個(gè)方向:
  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
  • 獲取
    通過[UIDevice currentDevice].orientation獲取當(dāng)前設(shè)備的方向
    當(dāng)關(guān)閉了系統(tǒng)的橫豎屏切換開關(guān)题翰,即系統(tǒng)層級(jí)只允許豎屏?xí)r,再通過上述方式獲取到的設(shè)備方向?qū)⒅皇?code>UIDeviceOrientationPortrait窖张。

UIDeviceOrientation是硬件設(shè)備的方向幕随,是隨著硬件自身改變的,只能取值宿接,不能設(shè)置赘淮。

2、UIInterfaceOrientation界面的顯示方向

  • 簡(jiǎn)介
    UIInterfaceOrientation即我們看到的視圖的Orientation睦霎,可以理解為statusBar所在的方向梢卸,是一個(gè)二維空間,有四個(gè)方向:
    UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
  • 獲取
    1.viewController. interfaceOrientation該方法在 iOS8之后廢除副女。
    2.[UIApplication sharedApplication].statusBarOrientation即狀態(tài)欄的方向蛤高。

  • 關(guān)聯(lián)
    UIDeviceOrientationUIInterfaceOrientation是兩個(gè)互不相干的屬性。
    其中一個(gè)并不會(huì)隨另外一個(gè)變化而變化碑幅。但大多數(shù)情況下戴陡,兩者會(huì)一起出現(xiàn),主要是為了達(dá)到視覺的統(tǒng)一沟涨。
    注意:
    UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
    兩者是相反的恤批,但是根據(jù)官方文檔,如下所示裹赴,我們發(fā)現(xiàn)兩者的方向是一致的喜庞。

    DeviceOrientation.png

    InterfaceOrientation.png

經(jīng)測(cè)試發(fā)現(xiàn)如下結(jié)論。
當(dāng)device處于UIInterfaceOrientationLandscapeLeft的狀態(tài)篮昧,即相當(dāng)于設(shè)備向左旋轉(zhuǎn)赋荆,要想達(dá)到視覺上的統(tǒng)一,頁(yè)面應(yīng)該向右旋轉(zhuǎn)懊昨,即[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeRight];其實(shí)設(shè)置完之后窄潭,再去獲取UIInterfaceOrientation發(fā)現(xiàn)得到的是UIInterfaceOrientationLandscapeLeft,與官方文檔并不矛盾。
這里其實(shí)是兩個(gè)概念,一是旋轉(zhuǎn)的方向嫉你,二是所處的方向月帝,使用是要當(dāng)心了!

  • 擴(kuò)展
    UIInterfaceOrientationMask
    這是ios6之后新增的一組枚舉值幽污,使用組合時(shí)更加方便嚷辅,使用時(shí)根據(jù)返回值類型選擇正確的格式,避免可能出現(xiàn)的bug
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),
}

3距误、UIInterfaceOrientation的控制

3.1 被動(dòng)控制

所謂的被動(dòng)控制簸搞,即支持自動(dòng)旋轉(zhuǎn)Autorotate,UIInterfaceOrientation會(huì)隨著UIDeviceOrientation的改變而自動(dòng)改變准潭,以達(dá)到視覺上的統(tǒng)一趁俊。是系統(tǒng)自動(dòng)控制的,我們只能控制其能夠支持自動(dòng)旋轉(zhuǎn)的方向刑然,使其在有些方向上可以跟隨旋轉(zhuǎn)寺擂,有些方向上不能。主要有以下三種方式

  • 3.1.1 【Targets】中設(shè)置
    【General】 -->【Deployment Info】-->【Device Orientation】
    Device Orientation.png

    這里雖然命名為DeviceOrientation,但實(shí)際上這里表示其界面支持的自動(dòng)旋轉(zhuǎn)的方向泼掠。
    為什么這么說呢怔软,因?yàn)檫@個(gè)地方的設(shè)置的值和info.plist文件里Supported interface orientations值是同步的,修改其中一個(gè)择镇,另一個(gè)也會(huì)隨之改變挡逼。另外,不論我們這里怎么勾選沐鼠,對(duì)程序當(dāng)中獲取當(dāng)前設(shè)備的orientation是沒有影響的挚瘟。
Supported interface orientations.png
  • 3.1.2 UIWindow設(shè)置
    iOS6的UIApplicationDelegate提供了下述方法叹谁,能夠指定UIWindow中的界面的屏幕方向:

    - (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window NS_AVAILABLE_IOS(6_0);
    該方法默認(rèn)值為Info.plist中配置的Supported interface orientations項(xiàng)的值饲梭。

  • 3.1.3UIViewController設(shè)置
    通過三個(gè)代理方法設(shè)置

//Interface的方向是否會(huì)跟隨設(shè)備方向自動(dòng)旋轉(zhuǎn),如果返回NO,后兩個(gè)方法不會(huì)再調(diào)用
- (BOOL)shouldAutorotate {
    return YES;
}
//返回直接支持的方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskPortrait;
}
//返回最優(yōu)先顯示的屏幕方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationPortrait;
}

解釋:
1.第二個(gè)方法焰檩,在iPad上的默認(rèn)返回值是UIInterfaceOrientationMaskAll憔涉,iPhone上的默認(rèn)返回值是UIInterfaceOrientationMaskAllButUpsideDown
2.在前面DeviceOrientation即使全部勾選了析苫,若要iPhone支持UpsideDown兜叨,也要在viewcontroller里重寫第二個(gè)方法。返回包含UpsideDown的方向衩侥;
3.第三個(gè)方法国旷,比如同時(shí)支持PortraitLandscape方向,但想優(yōu)先顯示Landscape方向茫死,那軟件啟動(dòng)的時(shí)候就會(huì)先顯示Landscape跪但,在手機(jī)切換旋轉(zhuǎn)方向的時(shí)候仍然可以在PortraitLandscape之間切換;

  • 3.1.4 總結(jié)
    1.這三種方式控制規(guī)則的交集就是一個(gè)viewController的最終支持的方向峦萎;
    如果最終的交集為空屡久,在iOS6以后會(huì)拋出UIApplicationInvalidInterfaceOrientationException崩潰異常忆首。
    2.如果關(guān)閉了系統(tǒng)的橫豎屏切換開關(guān),即系統(tǒng)層級(jí)只允許豎屏?xí)r被环,再通過上述方式獲取到的設(shè)備方向?qū)⑹?code>UIDeviceOrientationPortrait糙及。UIInterfaceOrientation也將不會(huì)改變。
    3.第三種方式只有在當(dāng)前viewControllerwindowrootViewController筛欢〗牵或者是通過presentModalViewController而顯示出來的.才會(huì)生效。作用于viewController及其childViewController版姑。否則UIKit并不會(huì)執(zhí)行上述方法揣钦。

  • 3.1.5 靈活控制
    上述方法基本上可以認(rèn)為是一種全局設(shè)置,實(shí)際項(xiàng)目中可能會(huì)是一個(gè)或幾個(gè)頁(yè)面需要單獨(dú)控制漠酿。通過UIViewController的三個(gè)方法設(shè)置Orientation時(shí)冯凹,只有在是windowrootViewController或者modal模式下才生效。且作用于其childViewController單獨(dú)設(shè)置某個(gè)viewController并沒有效果炒嘲。這種情況主要可以通過下面幾種方法解決宇姚。

  1. 在rootViewController里加判斷
  - (UIInterfaceOrientationMask)supportedInterfaceOrientations{
    if([[self topViewController] isKindOfClass:[subViewController class]])  
        return UIInterfaceOrientationMaskAllButUpsideDown;  
    else  
        return UIInterfaceOrientationMaskPortrait;
  }
  1. UINavigationControllerUITabBarController里重寫

-(UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return self.selectedViewController.supportedInterfaceOrientations;
}
-(BOOL)shouldAutorotate
{
    return [self.selectedViewController shouldAutorotate];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return [self.selectedViewController preferredInterfaceOrientationForPresentation];
}

UINavigationController 使用self.topViewController
UITabBarController 使用self.selectedViewController

然后在viewController重寫這三個(gè)方法,這樣就巧妙的繞開了UIKit只調(diào)用rootViewController的方法的規(guī)則. 把決定權(quán)交給了當(dāng)前正在顯示的viewController.

但是

這樣是可以在當(dāng)前viewController達(dá)到預(yù)期效果夫凸,但是在返回上一頁(yè)時(shí)浑劳,或者在當(dāng)前頁(yè)面不不支持的方向的上一頁(yè)進(jìn)來時(shí),不能立即達(dá)到預(yù)期狀態(tài)夭拌,需要設(shè)備方向更換一次才能恢復(fù)正常魔熏。

解決方案:

#pragma mark -UITabBarControllerDelegate
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
    [self presentViewController:[UIViewController new] animated:NO completion:^{
        [self dismissViewControllerAnimated:NO completion:nil];
    }];
}
#pragma mark -UINavigationControllerDelegate
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    [self presentViewController:[UIViewController new] animated:NO completion:^{
        [self dismissViewControllerAnimated:NO completion:nil];
    }];
}

這樣就會(huì)觸發(fā)-(BOOL)shouldAutorotate方法和
-(UIInterfaceOrientationMask)supportedInterfaceOrientations方法,但是會(huì)閃一下鸽扁,依然不完美蒜绽。

  1. 把需要單獨(dú)設(shè)置的viewController添加在一個(gè)獨(dú)立的navgationController
    這種方法不適用rootViewControllerUITabBarController的, 不推薦使用。
3.2 主動(dòng)控制

所謂主動(dòng)控制即不讓其自動(dòng)旋轉(zhuǎn)Autorotate == NO,方向自行控制桶现。主要有兩種方式

  • 3.2.1 UIView.transform
    代碼如下:
//設(shè)置statusBar
[[UIApplication sharedApplication] setStatusBarOrientation:orientation];

//計(jì)算旋轉(zhuǎn)角度
float arch;
if (orientation == UIInterfaceOrientationLandscapeLeft)
    arch = -M_PI_2;
else if (orientation == UIInterfaceOrientationLandscapeRight)
    arch = M_PI_2;
else
    arch = 0;

//對(duì)navigationController.view 進(jìn)行強(qiáng)制旋轉(zhuǎn)
self.navigationController.view.transform = CGAffineTransformMakeRotation(arch);
self.navigationController.view.bounds = UIInterfaceOrientationIsLandscape(orientation) ? CGRectMake(0, 0, SCREEN_HEIGHT, SCREEN_WIDTH) : initialBounds;

注意:

  1. statusBar不會(huì)自己旋轉(zhuǎn)躲雅,這里要首先設(shè)置statusBar的方向。iOS9后setStatusBarOrientation方法廢除
  2. 我們這里選擇的是self.navigationController進(jìn)行旋轉(zhuǎn)骡和,當(dāng)然也可以是self.view或者self.window,都可以相赁,最好是全屏的view.
  3. 我們需要顯示的設(shè)置bounds,UIKit并不知道你偷偷摸摸干了這些事情。
  4. -(BOOL)shouldAutorotate方法慰于,應(yīng)返回NO

在iOS 9 之后橫屏?xí)r钮科,狀態(tài)欄會(huì)消失。

解決方法:確保Info.plist中的【View controller-based status bar appearance】YES婆赠,然后重寫viewController- (BOOL)prefersStatusBarHidden绵脯,返回值是NO。詳細(xì)參考iOS-UIStatusBar詳細(xì)總結(jié)

  • 3.2.2 強(qiáng)制旋轉(zhuǎn)setOrientation
    setOrientation 在iOS3以后變?yōu)樗接蟹椒耍荒苤苯尤フ{(diào)用此方法桨嫁,否則后果就是被打回植兰。
    不能直接調(diào)用,但是可以間接的去調(diào)用璃吧,下面的方法就是利用 KVO機(jī)制去間接調(diào)用楣导,多次驗(yàn)證不會(huì)被打回,放心畜挨!
-(void)viewWillAppear:(BOOL)animated{
//首先設(shè)置UIInterfaceOrientationUnknown欺騙系統(tǒng)筒繁,避免可能出現(xiàn)直接設(shè)置無效的情況
    NSNumber *orientationUnknown = [NSNumber numberWithInt:UIInterfaceOrientationUnknown];
    [[UIDevice currentDevice] setValue:orientationUnknown forKey:@"orientation"];
    
    NSNumber *orientationTarget = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
    [[UIDevice currentDevice] setValue:orientationTarget forKey:@"orientation"];
}

參考文章:
iOS屏幕旋轉(zhuǎn)學(xué)習(xí)筆記
[iOS]Orientation 想怎么轉(zhuǎn)就怎么轉(zhuǎn)
iOS 知識(shí)小集(橫豎屏切換)
iOS強(qiáng)制改變物理設(shè)備方向的進(jìn)階方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市巴元,隨后出現(xiàn)的幾起案子毡咏,更是在濱河造成了極大的恐慌,老刑警劉巖逮刨,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呕缭,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡修己,警方通過查閱死者的電腦和手機(jī)恢总,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來睬愤,“玉大人片仿,你說我怎么就攤上這事∮热瑁” “怎么了砂豌?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)光督。 經(jīng)常有香客問我阳距,道長(zhǎng),這世上最難降的妖魔是什么可帽? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任娄涩,我火速辦了婚禮,結(jié)果婚禮上映跟,老公的妹妹穿的比我還像新娘。我一直安慰自己扬虚,他們只是感情好努隙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著辜昵,像睡著了一般荸镊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天躬存,我揣著相機(jī)與錄音张惹,去河邊找鬼。 笑死岭洲,一個(gè)胖子當(dāng)著我的面吹牛宛逗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盾剩,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼雷激,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了告私?” 一聲冷哼從身側(cè)響起屎暇,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎驻粟,沒想到半個(gè)月后根悼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜀撑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年番挺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屯掖。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡玄柏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出贴铜,到底是詐尸還是另有隱情粪摘,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布绍坝,位于F島的核電站徘意,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏轩褐。R本人自食惡果不足惜椎咧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望把介。 院中可真熱鬧勤讽,春花似錦、人聲如沸拗踢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)巢墅。三九已至诸狭,卻和暖如春券膀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背驯遇。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工芹彬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人叉庐。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓舒帮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親眨唬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子会前,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容