iOS屏幕旋转 浅析
一、兩種orientation
了解屏幕旋轉(zhuǎn)首先需要區(qū)分兩種orientation
1、device orientation
設(shè)備的物理方向,由類型UIDeviceOrientation表示,當(dāng)前設(shè)備方向獲取方式:
| 1 | [UIDevice currentDevice].orientation |
該屬性的值一般是與當(dāng)前設(shè)備方向保持一致的,但須注意以下幾點(diǎn):
①文檔中對(duì)該屬性的注釋:
| 1 | @property(nonatomic,readonly) UIDeviceOrientation orientation; // return current device orientation. this will return UIDeviceOrientationUnknown unless device orientation notifications are being generated. |
所以更推薦下面這種用法:
| 1 2 3 4 5 6 7 | if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) { [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; } NSLog(@"%d",[UIDevice currentDevice].orientation); [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; |
②系統(tǒng)橫豎屏開(kāi)關(guān)關(guān)閉時(shí)
如果關(guān)閉了系統(tǒng)的橫豎屏切換開(kāi)關(guān),即系統(tǒng)層級(jí)只允許豎屏?xí)r,再通過(guò)上述方式獲取到的設(shè)備方向?qū)⒂肋h(yuǎn)是UIDeviceOrientationUnknown。可以通過(guò)Core Motion中的CMMotionManager來(lái)獲取當(dāng)前設(shè)備方向。
2、interface orientation
界面顯示的方向,由類型UIInterfaceOrientation表示。當(dāng)前界面顯示方向有以下兩種方式獲取:
| 1 2 | NSLog(@"%d",[UIApplication sharedApplication].statusBarOrientation); NSLog(@"%d",viewController.interfaceOrientation); |
即可以通過(guò)系統(tǒng)statusBar的方向或者viewController的方向來(lái)獲取當(dāng)前界面方向。
3、二者區(qū)別
通過(guò)UIDevice獲取到的設(shè)備方向在手機(jī)旋轉(zhuǎn)時(shí)是實(shí)時(shí)的,通過(guò)UIApplication的statusBar或者viewController獲取到的界面方向在下述方法:
| 1 | - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration: |
調(diào)用以后才會(huì)被更改成最新的值。
二、相關(guān)枚舉定義
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多了兩個(gè)定義:UIDeviceOrientationFaceUp和UIDeviceOrientationFaceDown,分別表示手機(jī)水平放置,屏幕向上和屏幕向下。
三、相關(guān)方法
1、iOS5中控制屏幕旋轉(zhuǎn)的方法:
| 1 2 | // Applications should use supportedInterfaceOrientations and/or shouldAutorotate.. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation NS_DEPRECATED_IOS(2_0, 6_0); |
如果打算支持toInterfaceOrientation對(duì)應(yīng)的方向就返回YES,否則返回NO。
2、iOS6中控制屏幕旋轉(zhuǎn)相關(guān)方法:
| 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); |
第一個(gè)方法決定是否支持多方向旋轉(zhuǎn)屏,如果返回NO則后面的兩個(gè)方法都不會(huì)再被調(diào)用,而且只會(huì)支持默認(rèn)的UIInterfaceOrientationMaskPortrait方向;
第二個(gè)方法直接返回支持的旋轉(zhuǎn)方向,該方法在iPad上的默認(rèn)返回值是UIInterfaceOrientationMaskAll,iPhone上的默認(rèn)返回值是UIInterfaceOrientationMaskAllButUpsideDown,詳情見(jiàn)官方Q&A文檔;
第三個(gè)方法返回最優(yōu)先顯示的屏幕方向,比如同時(shí)支持Portrait和Landscape方向,但想優(yōu)先顯示Landscape方向,那軟件啟動(dòng)的時(shí)候就會(huì)先顯示Landscape,在手機(jī)切換旋轉(zhuǎn)方向的時(shí)候仍然可以在Portrait和Landscape之間切換;
3、attemptRotationToDeviceOrientation方法
從iOS5開(kāi)始有了這個(gè)新方法:
| 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); |
該方法的使用場(chǎng)景是interface orientation和device orientation不一致,但希望通過(guò)重新指定interface orientation的值,立即實(shí)現(xiàn)二者一致;如果這時(shí)只是更改了支持的interface orientation的值,沒(méi)有調(diào)用attemptRotationToDeviceOrientation,那么下次device orientation變化的時(shí)候才會(huì)實(shí)現(xiàn)二者一致,關(guān)鍵點(diǎn)在于能不能立即實(shí)現(xiàn)。
舉個(gè)例子:
假設(shè)當(dāng)前的interface orientation只支持Portrait,如果device orientation變成Landscape,那么interface orientation仍然顯示Portrait;
如果這時(shí)我們希望interface orientation也變成和device orientation一致的Landscape,以iOS6為例,需要先將supportedInterfaceOrientations的返回值改成Landscape,然后調(diào)用attemptRotationToDeviceOrientation方法,系統(tǒng)會(huì)重新詢問(wèn)支持的interface orientation,已達(dá)到立即更改當(dāng)前interface orientation的目的。
四、如何決定interface orientation
1、全局控制
Info.plist文件中,有一個(gè)Supported interface orientations,可以配置整個(gè)應(yīng)用的屏幕方向,此處為全局控制。
2、UIWindow
iOS6的UIApplicationDelegate提供了下述方法,能夠指定 UIWindow 中的界面的屏幕方向:
| 1 | - (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window NS_AVAILABLE_IOS(6_0); |
該方法默認(rèn)值為Info.plist中配置的Supported interface orientations項(xiàng)的值。
iOS中通常只有一個(gè)window,所以此處的控制也可以視為全局控制。
3、controller
只有以下兩種情況:
- 當(dāng)前controller是window的rootViewController
- 當(dāng)前controller是modal模式的
時(shí),orientations相關(guān)方法才會(huì)起作用(才會(huì)被調(diào)用),當(dāng)前controller及其所有的childViewController都在此作用范圍內(nèi)。
4、最終支持的屏幕方向
前面所述的3種控制規(guī)則的交集就是一個(gè)controller的最終支持的方向;
如果最終的交集為空,在iOS6以后會(huì)拋出UIApplicationInvalidInterfaceOrientationException崩潰異常。
四、強(qiáng)制屏幕旋轉(zhuǎn)
如果interface和device方向不一樣,想強(qiáng)制將interface旋轉(zhuǎn)成device的方向,可以通過(guò)attemptRotationToDeviceOrientation實(shí)現(xiàn),但是如果想將interface強(qiáng)制旋轉(zhuǎn)成任一指定方向,該方式就無(wú)能為力了。
不過(guò)聰明的開(kāi)發(fā)者們總能想到解決方式:
1、私有方法
| 1 | [[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait]; |
但是現(xiàn)在蘋(píng)果已經(jīng)將該方法私有化了,越獄開(kāi)發(fā)的同學(xué)可以試試,或者自己想法子騙過(guò)蘋(píng)果審核吧。
2、旋轉(zhuǎn)view的transform
也可以通過(guò)旋轉(zhuǎn)view的transform屬性達(dá)到強(qiáng)制旋轉(zhuǎn)屏幕方向的目的,但個(gè)人感覺(jué)這不是靠譜的思路,可能會(huì)帶來(lái)某些詭異的問(wèn)題。
3、主動(dòng)觸發(fā)orientation機(jī)制
要是能主動(dòng)觸發(fā)系統(tǒng)的orientation機(jī)制,調(diào)用orientation相關(guān)方法,使新設(shè)置的orientation值起作用就好了。這樣只要提前設(shè)置好想要支持的orientation,然后主動(dòng)觸發(fā)orientation機(jī)制,便能實(shí)現(xiàn)將interface orientation旋轉(zhuǎn)至任意方向的目的。
萬(wàn)能的stackoverflow上提供了一種主動(dòng)觸發(fā)的方式:
在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]; |
這種方式會(huì)觸發(fā)UIKit重新調(diào)用controller的orientation相關(guān)方法,以達(dá)到在device方向不變的情況下改變interface方向的目的。
雖然不優(yōu)雅,但卻能解決問(wèn)題,湊合吧。。
PS:
話說(shuō)iOS8中的屏幕旋轉(zhuǎn)相關(guān)方法又變化了,表示適配起來(lái)很蛋疼。。。
五、參考文檔
- Why won’t my UIViewController rotate with the device?;
- How to force a UIViewController to Portait orientation in iOS 6
- IOS Orientation, 想怎么轉(zhuǎn)就怎么轉(zhuǎn)
- iOS 屏幕方向那點(diǎn)事兒
轉(zhuǎn)自:http://foggry.com/blog/2014/08/08/ping-mu-xuan-zhuan-xue-xi-bi-ji/
總結(jié)
以上是生活随笔為你收集整理的iOS屏幕旋转 浅析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Android程序打开和关闭输入法
- 下一篇: 【转载】关于RabbitMQ的高可用性