關於橫豎屏適配 也沒做過,今天讀別人的源碼,遇到了。為了了解清楚,就系統的學習一下。
一 橫豎屏方向枚舉
關於橫豎屏一共有三種枚舉 UIInterfaceOrientation UIInterfaceOrientationMask UIDeviceOrientation。
1.1 UIInterfaceOrientation與UIDeviceOrientation
為什么這兩個放在一起說,好吧,你看看下面這個枚舉定義:
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) { UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown, UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait, UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown, UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight, UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft } __TVOS_PROHIBITED;
對於iOS
設備來講,屏幕狀態由以上五種狀態。上下翻轉還是很好區分的,左右旋轉可能就不是很好區分。
如果我們仔細看枚舉值的話 會發現一些問題
在處於豎屏和上下翻轉的狀態下這兩個枚舉值是一樣的,而處於橫屏時這兩個枚舉值剛好相反
所以在有時你發現跟你預期的翻轉方向不一樣的時候,可能你用錯了枚舉。
UIDeviceOrientation 是設備的當前所處的方向,而且事實上他有六個值
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 } __TVOS_PROHIBITED;
分別對應iPhone未知方向,豎直,上下反轉,向左旋轉,向右旋轉,屏幕朝上,屏幕朝下 那么如何區分左右呢
UIDeviceOrientationLandscapeLeft,home鍵在右側
UIDeviceOrientationLandscapeRight,home鍵在左側
所以,UIDevice
顧名思義,事實上是用來判斷設備方向的。
UIInterfaceOrientation 即當前頁面的方向。
在設備進行橫屏旋轉的時候,為了橫屏時上下不翻轉,所以當Device處於Left時,界面應該是Right旋轉。這樣才不會使橫屏時內容上下翻轉。
所以對於橫豎屏適配,使用的枚舉大家一定要看好,使用UIInterfaceOrientation
。不要搞反。
1.2 UIInterfaceOrientationMask
這是iOS6之后新增的一組枚舉值
typedef NS_OPTIONS(NSUInteger, UIInterfaceOrientationMask) { UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait), UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft),// home 鍵 在右 UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight),//home鍵 在左 UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown), UIInterfaceOrientationMaskLandscape = (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight), UIInterfaceOrientationMaskAll = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown), UIInterfaceOrientationMaskAllButUpsideDown = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight), } __TVOS_PROHIBITED;
事實上我們在橫豎屏適配時,最常用的是這個枚舉。這個枚舉詳細的列舉了各種你需要的情況
二 開啟橫豎屏的權限
可以看到這種勾選方式允許你進行四個方向的配置,並且這種勾選方式會直接在你的項目plist文件中添加
但是由於在這里配置是對項目啟動時lanuch界面產生影響,而往往我們又沒有對lanuch進行橫豎屏適配,所以在這個時候我們就需要使用第二種方式進行配置。
在項目中的AppDelegate文件中進行配置。
#pragma mark - InterfaceOrientation //應用支持的方向 - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window { return UIInterfaceOrientationMaskAllButUpsideDown; }
搭配UIInterfaceOrientationMask
使用,你可以很方便的讓你項目開啟你所需要的橫豎屏權限和限制條件。
三 在VC中如何控制橫豎屏
我們都知道MVC架構,那么顯而易見,在我們開啟了項目的橫豎屏的限制之后,需要在ViewController
進行相應的配置,才能真正實現橫豎屏。
開啟橫豎屏,我們需要在VC中添加如下代碼
// 設備支持方向 - (UIInterfaceOrientationMask)supportedInterfaceOrientations { return UIInterfaceOrientationMaskAll; } // 默認方向 - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { return UIInterfaceOrientationPortrait; // 或者其他值 balabala~ }
而對於橫豎屏,手機端一般有兩種情況,一種是手機沒有開啟橫豎屏鎖定,用戶將手機橫屏時觸發的。對於第一種情況,我們只需要在VC中添加:
// 開啟自動轉屏 - (BOOL)shouldAutorotate { return YES; }
另一種是我們在項目中的某些條件下強行讓屏幕橫屏,例如大圖預覽,視頻播放,等等。而對於這種情況,我們可以使用下面👇這兩種方法,都可以實現效果:
- (void)setInterfaceOrientation:(UIInterfaceOrientation)orientation { 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 = orientation; [invocation setArgument:&val atIndex:2]; [invocation invoke]; } }
- (void)setInterfaceOrientation:(UIDeviceOrientation)orientation { if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) { [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:orientation] forKey:@"orientation"]; } }
PS:這兩個方法只有在- (BOOL)shouldAutorotate( return YES; )
時,才會生效。並且請注意使用的枚舉值的不同。
四.橫豎屏控制優先級
在我們接手一個項目后,說要添加一個某個界面橫豎屏需求時,發現按照上面的方式配置了一圈,發現還是轉!不!成!功!What F***!!!
事實上在這里我們要了解一個問題,就是關於橫豎屏控制的優先級。對於限於VC
范圍來講優先級最高的是當前的window
的rootViewController
,而往往我們的項目結構是容器視圖控制器控制VC
,tabBarController
控制navigationController
之后是VC
,而橫豎屏控制的優先級也是跟你的項目架構一樣。而且是一旦優先級高的關閉了橫豎屏配置,優先級低的無論如何配置都無法做到橫豎屏。所以在你接受這個需求的時候,你需要看一下根視圖的配置。
對於這種情況,我們有兩種處理方式,一種是通過模態的方式跳轉的下個VC
,這個VC
是隔離出來的,不在你之前的容器里,不會受到rootViewController
的影響。
而另一種我們需要改造一下根視圖的配置:
-(BOOL)shouldAutorotate { return [[self.viewControllers lastObject] shouldAutorotate]; } - (UIInterfaceOrientationMask)supportedInterfaceOrientations { return [[self.viewControllers lastObject] supportedInterfaceOrientations]; } - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation]; }
或者
-(BOOL)shouldAutorotate { if ([[self.viewControllers lastObject]isKindOfClass:[NewViewController class]]) { return YES; } return NO; } - (UIInterfaceOrientationMask)supportedInterfaceOrientations { if ([[self.viewControllers lastObject]isKindOfClass:[NewViewController class]]) { return UIInterfaceOrientationMaskLandscapeLeft; } return UIInterfaceOrientationMaskPortrait; } - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { if ([[self.viewControllers lastObject]isKindOfClass:[NewViewController class]]) { return UIInterfaceOrientationLandscapeLeft; } return UIInterfaceOrientationPortrait; }
可以看到我們通過獲取push棧中的最后一個VC
的屬性,或指定特殊的VC
來進行rootViewController
的橫豎屏設置。
當然也可以通過NSNotificationCenter
或者NSUserDefaults
的方式對這里的值進行設置,在這里我就不過多贅述了。
總之要知道優先級的問題,general
== appDelegate
>> rootViewController
>> nomalViewController
明白了權限的優先級以及開啟的方法我想轉屏就很顯而易見了。
五.橫豎屏適配
事實上旋轉屏幕成功,對於iOS橫豎屏問題我們只是完成了一半。另一半就是UI適配問題,其實這個要說起來就比較麻煩了,有些時候有很多case需要針對對應的業務條件來定制。但是無外乎幾種實現思路。這里博主給大家拋幾塊磚哈:
首先我們要知道,當發生轉屏事件時,系統的回調方法是:
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { if (size.width > size.height) { // 橫屏 // 橫屏布局 balabala } else { // 豎屏布局 balabala } }
首推的方式是使用約束布局,在使用約束布局時,橫豎屏轉換時,在通常情況下約束條件會很相似,所以在布局上會極大的減少代碼量。其次如果有個別的特殊問題,可以在上面的回調方法里面進行微調。
其次,對於轉屏后,[UIScreen mainScreen].bounds.size
以及self.view.frame.size
的寬高系統會自動調換。即在橫屏的時候width > height
。所以在我們進行常規布局的時候我們可以選擇控件的frame
屬性都與這兩個屬性進行比例換算。這樣在當橫豎屏轉換的時候,重布局時,也會適應成對應屏幕下的布局。同樣有需要特殊處理的布局,在上面的回調方法中進行細節微調即可。
對於子視圖,在橫豎屏切換時,會觸發子視圖重布局的方法:
- (void)layoutSubviews { [super layoutSubviews]; // 通過狀態欄電池圖標來判斷屏幕方向 if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationMaskPortrait) { // 豎屏 balabala } else { // 橫屏 balabala } }
下面推薦幾個符合的博客