iOS 橫豎屏適配


關於橫豎屏適配 也沒做過,今天讀別人的源碼,遇到了。為了了解清楚,就系統的學習一下。

一 橫豎屏方向枚舉

關於橫豎屏一共有三種枚舉 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范圍來講優先級最高的是當前的windowrootViewController,而往往我們的項目結構是容器視圖控制器控制VCtabBarController控制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
    }
}
當然我只是說了幾種比較簡單的處理方式,和應對方法,對於整個項目都需要橫豎屏適配的,我想還是一個比較復雜的過程。在實在處理不了的問題上,也可以通過寫兩套布局的方式來處理。至於過場動畫,理論上如果你用約束和我說的比例布局的方式來寫,基本系統會自動幫你這個問題給處理掉。但如果兩種布局差距很大,你用了兩套完全不同的布局,那這個你可能就要傷腦筋了。哈哈哈。不過有一些情況處理要求不嚴格的話可以使用截圖過場大法來解決。不過這個就不是本文的設計范圍了。大家如果感興趣可以自己google一下。當然我日后的文章也可能會寫到這了。到時候再來這里修改。

下面推薦幾個符合的博客
//iPad橫豎屏下的代碼適配
 

 

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM