關于橫豎屏適配,有一句說一句,坑挺深的。之前做Vision和畢設的時候就處理過橫豎屏問題,不過當時的功力太淺,明顯沒有處理明白。所以這次在公司項目中又一次遇到了這種橫豎屏的需求,自然要認真的搞一哈,順便總結一下分享給大家。其實在我理解上,只要明白以下幾點,橫豎屏處理上并不是問題。大家按需跳轉吧:
1.橫豎屏方向枚舉
關于橫豎屏一共有三種枚舉,UIInterfaceOrientation,UIInterfaceOrientationMask,UIDeviceOrientation。
1.1 UIInterfaceOrientation與UIDeviceOrientation
為什么這兩個放在一起說,好吧,你看看下面這個枚舉定義:
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown,
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait,
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
}
我相信你應該看出了點東西了把,對于iOS
設備來講,屏幕狀態由以上五種狀態。上下翻轉還是很好區分的,左右旋轉可能就不是很好區分。
這里有個坑!!!
高能預警:
請仔細觀察上面的枚舉值。
在處于豎屏和上下翻轉的狀態下這兩個枚舉值是一樣的,而當處于橫屏時,這兩個值剛好相反。
所以在有時你發現跟你預期的翻轉方向不一樣的時候,可能你用錯了枚舉。
UIDeviceOrientation
是設備的當前所處的方向,而且事實上它有6個值,
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
}
分別對應iPhone
未知方向,豎直,上下反轉,向左旋轉,向右旋轉,屏幕朝上,屏幕朝下。關于橫屏如何去分左右,其實API中的注釋已經說明,當處于UIDeviceOrientationLandscapeLeft
,home鍵在右側,當處于UIDeviceOrientationLandscapeRight
,home鍵在左側。
所以,UIDevice
顧名思義,事實上是用來判斷設備方向的。
UIInterfaceOrientation
即當前頁面的方向。
在設備進行橫屏旋轉的時候,為了橫屏時上下不翻轉,所以當Device處于Left時,界面應該是Right旋轉。這樣才不會使橫屏時內容上下翻轉。所以我想你應該明白了為什么在處于橫屏時為什么他們倆的值是剛好相反的。
所以對于橫豎屏適配,使用的枚舉大家一定要看好,使用UIInterfaceOrientation
。不要搞反。
1.2 UIInterfaceOrientationMask
其實蘋果大大還是給了我們更清晰和方便的枚舉如下:
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),
}
事實上我們在橫豎屏適配時,最常用的是這個枚舉。這個枚舉詳細的列舉了各種你需要的情況。我就不贅述了。官方的命名還是很舒服很好理解的。
2.開啟橫豎屏權限
開啟橫豎屏的方式有兩種,一種是在項目中直接進行勾選,
可以看到這種勾選方式允許你進行四個方向的配置,并且這種勾選方式會直接在你的項目plist文件中添加
但是由于在這里配置是對項目啟動時lanuch界面產生影響,而往往我們又沒有對lanuch進行橫豎屏適配,所以在這個時候我們就需要使用第二種方式進行配置。
在項目中的AppDelegate文件中進行配置。
#pragma mark - InterfaceOrientation //應用支持的方向
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return UIInterfaceOrientationMaskAllButUpsideDown;
}
搭配UIInterfaceOrientationMask
使用,你可以很方便的讓你項目開啟你所需要的橫豎屏權限和限制條件。
3.在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; )
時,才會生效。并且請注意使用的枚舉值的不同。
4.橫豎屏控制優先級
在我們接手一個項目后,說要添加一個某個界面橫豎屏需求時,發現按照上面的方式配置了一圈,發現還是轉!不!成!功!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
明白了權限的優先級以及開啟的方法我想轉屏就很顯而易見了。
5.橫豎屏適配
事實上旋轉屏幕成功,對于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
}
}
當然我只是說了幾種比較簡單的處理方式,和應對方法,對于整個項目都需要橫豎屏適配的,我想還是一個比較復雜的過程。在實在處理不了的問題上,也可以通過寫兩套布局的方式來處理。至于過場動畫,理論上如果你用約束和我說的比例布局的方式來寫,基本系統會自動幫你這個問題給處理掉。但如果兩種布局差距很大,你用了兩套完全不同的布局,那這個你可能就要傷腦筋了。哈哈哈。不過有一些情況處理要求不嚴格的話可以使用截圖過場大法來解決。不過這個就不是本文的設計范圍了。大家如果感興趣可以自己google一下。當然我日后的文章也可能會寫到這了。到時候再來這里修改。
6.總結
iOS橫豎屏適配,確實有很多坑,當然,有些坑是系統的;而有些坑,是因為我們的無知而造成的。所以多看多學多做多理解,必然能讓你學到更多,增強填坑的硬實力。而對于橫豎屏適配這塊,轉屏并不難,難的是橫豎屏布局適配。博主只是簡單的說了一些思路,至于實現起來,還是要針對對應的需求來進行處理。下面??給大家鏈接幾個博主覺得還不錯的關于適配的文章,大家可以看看: