[iOS]代碼控制APP中視圖橫屏/豎屏切換

不少時候,項目中都有這樣的需求:APP中以豎屏爲主,個別界面會要求橫屏顯示,或者要根據用戶的手機朝向自動切換橫豎屏;下面就來詳細講解,在項目中怎麼使用代碼來控制APP的界面轉換.
首先,要想APP支持多個方向,須要在工程進行設置支持的朝向:
General-->Deployment Info-->Device Orientation中進行設置
設置APP支持的手機方向git

這樣,就能夠在項目中使用代碼來控制頁面的朝向了,在這以前,須要知道控制視圖是否可以自動旋轉,以及支持的朝向,是經過哪些方法來控制的?
其實,主要使用的是下面三個方法:github

// New Autorotation support.  
//是否自動旋轉,返回YES能夠自動旋轉  
- (BOOL)shouldAutorotate NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;  
//返回支持的方向  
- (UIInterfaceOrientationMask)supportedInterfaceOrientations NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;  
// Returns interface orientation masks.  
//這個是返回優先方向  
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;

通常狀況下,實現前兩個方法便可!這些都是UIViewController的實例方法,直接在須要設置的控制器內重寫上面的方法便可;
有時候,你會發現,只有在APP啓動時出現的第一個界面內重寫上面的兩個方法,會有效果;而在其餘的界面重寫,並無效果.這是由於,程序啓動後的第一個界面是程序的跟視圖控制器,而這些設置,必須是影響了跟視圖中的相應設置,纔會有效果;
假如,你在跟視圖中進行了以下設置:數組

- (BOOL)shouldAutorotate{  
    return NO;  
}

那麼,在整個項目中,不管你如何設置,都不會有自動旋轉屏幕的效果.
綜上可知,若是要改變某個視圖屏幕方向,須要告訴跟視圖,在跟視圖中修改成相應的設置便可;
因此,問題來了,怎麼告訴跟視圖呢?這要視狀況而定:app

1.當前viewController就是跟視圖控制器

這種狀況最簡單,直接在控制器中重寫上面的方法,設置是否支持自動旋轉,以及支持的方向便可:ide

- (BOOL)shouldAutorotate{
    return YES;
}

#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0
- (NSUInteger)supportedInterfaceOrientations
#else
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
#endif
{
    return UIInterfaceOrientationMaskAll;
}

像上面我設置爲支持自動旋轉,支持全部的朝向;
這種方式, 切換視圖的方式主要是模態(present), 只須要在相應的UIViewController裏重寫上面的方法便可.
可是這種狀況不多見...
對應的demo爲:LZInterfaceRotation中的Rotation-UIViewControllerpost

2.項目的跟視圖控制器是導航(UINavigationController)

這種狀況比較常見,也很簡單;通常使用的導航的時候,都不會去直接使用系統的UINavigationController,而會定義一個他的子類,作一些公共的設置:spa

#import <UIKit/UIKit.h>

@interface BaseNaviViewController : UINavigationController

@end

而後在導航的viewController裏面重寫上面的三個方法,這裏也有兩種方式來控制:code

2.1 設置最後一個push的viewController

這種方式,主要是用在,當push到某個視圖控制器時,要求他可以支持自動旋轉,或者強制轉爲橫屏;這裏主要用到了導航的viewControllers屬性,這是一個數組,數組的最後一個元素,就是最後push進去的viewController,因此,實現代碼以下:視頻

-(BOOL)shouldAutorotate  
{  
    return [[self.viewControllers lastObject] shouldAutorotate];  
      
}  
  
#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0  
- (NSUInteger)supportedInterfaceOrientations  
#else  
- (UIInterfaceOrientationMask)supportedInterfaceOrientations  
#endif  
{  
     return [[self.viewControllers lastObject] supportedInterfaceOrientations];  
}  
  
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {  
      
    return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];  
}

到這裏尚未結束,這裏只是獲取到了最後一個push的viewController,而後用它去調用了相應的方法,具體的方法實現,仍是須要在哪一個viewController裏進行設置的,也就是在須要特殊設置的viewController裏重寫上面的方法:server

-(BOOL)shouldAutorotate
{
    return YES;
}

#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0
- (NSUInteger)supportedInterfaceOrientations
#else
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
#endif
{
    return UIInterfaceOrientationMaskPortraitUpsideDown;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    
    return UIInterfaceOrientationPortraitUpsideDown;
}

其實這樣也就是告訴跟視圖控制器,這個界面我要特殊設置一下;
對應的demo爲: LZInterfaceRotation中的Rotation-UINavigationController

2.2設置指定的控制器

第一種方法是,只要push進去一個視圖控制器,均可以進行相應的設置,而這種方法是隻針對特定的控制器進行設置,例如這裏,我須要把SecondViewController 單獨設置爲支持橫屏,只須要將第一種方法中的導航裏的相應代碼作以下修改:

-(BOOL)shouldAutorotate
{
    if ([[self.viewControllers lastObject]isKindOfClass:[SecondViewController class]]) {
        return YES;
    }
    
    return NO;
}

#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0
- (NSUInteger)supportedInterfaceOrientations
#else
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
#endif
{
    if ([[self.viewControllers lastObject]isKindOfClass:[SecondViewController class]]) {
                return UIInterfaceOrientationMaskLandscapeLeft;
            }

    return UIInterfaceOrientationMaskPortrait;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    
    if ([[self.viewControllers lastObject]isKindOfClass:[SecondViewController class]]) {
        return UIInterfaceOrientationLandscapeLeft;
    }

    return UIInterfaceOrientationPortrait;
}

這樣的好處是,能夠控制指定的視圖控制器,也不用在指定的視圖控制器內再重寫上面的方法;
壞處就是,若是須要控制的視圖控制器比較多的話,加的判斷會多不少;沒有方式一靈活;
對應的demo爲: LZInterfaceRotation中的Rotation-UINavigationController副本

3.跟視圖控制器是UITabbarController

這種狀況比較常見,項目的跟視圖控制器是一個UITabBarController,這種狀況的實現相對來講要複雜一些,可是,明白了原理,老是有方法來實現的.歸根結底,也就是告訴跟視圖,我這個界面須要支持旋轉了;
因爲,在UITabBarController裏直接獲取到UIViewController比較困難,因此這裏我採用通知的形式,來獲取相應的設置狀態;
首先,設置一個全局的BOOL值,用於接收通知發送的參數:

#import <UIKit/UIKit.h>

@interface BaseTabBar : UITabBarController
{
    BOOL shouldAutorotate;  
}
@end

而後註冊一個通知:

//註冊旋轉屏幕的通知
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(autorotateInterface:) name:@"InterfaceOrientationNotification" object:nil];

實現通知方法:

-(void)autorotateInterface:(NSNotification *)notifition
{
    shouldAutorotate = [notifition.object boolValue];
}

而後在重寫的方法里加入判斷:

/** 
 * 
 *  @return 是否支持旋轉 
 */  
-(BOOL)shouldAutorotate  
{  
    NSLog(@"======%d",shouldAutorotate);  
   return shouldAutorotate;
}  
  
/** 
 *  適配旋轉的類型 
 * 
 *  @return 類型 
 */  
-(UIInterfaceOrientationMask)supportedInterfaceOrientations  
{  
    if (!shouldAutorotate) {  
        return UIInterfaceOrientationMaskPortrait;  
    }  
    return UIInterfaceOrientationMaskAllButUpsideDown;  
}

這裏我直接將支持的方向寫死了,只是判斷了一下是否支持自動旋轉屏幕,若是須要將支持的方向傳過來,能夠修改通知攜帶的參數;
最後在須要自動轉屏的控制器內發送通知:

[[NSNotificationCenter defaultCenter] postNotificationName:@"InterfaceOrientationNotification" object:@"YES"];

對應的demo爲: LZInterfaceRotation中的Rotation-UITabBarController

PS新增: 關於什麼時候發送通知
例若有這樣的需求: A豎屏, 不能自動旋轉, B 可橫屏可豎屏可自動旋轉, 並且在從B回到A的時候, 若是不是豎屏, 強制轉爲豎屏, 這就用到了下面強制轉屏的方法;

咱們在使用的時候確定是在BviewWillAppear方法裏發送通知告訴跟視圖要自動旋轉, 而在離開這個視圖時, 就有可能會在viewWillDisappear方法裏發送不能自動旋轉的通知, 這樣是不能實現效果的, 應該放在viewDidDisappear方法裏發送;
爲嚴謹起見, 建議在viewDidAppear方法裏發送能夠自動旋轉的通知, 在viewDidDisappear方法裏發送不能夠自動選擇的通知.
這裏使用的通知來告知跟視圖是否自動旋轉, 也可使用NSUserDefaults 來傳遞這個值, 或者單例均可以.

到此,對於控制屏幕的旋轉及方向,基本就介紹完了,若是有不足或者不對的地方,還請指正;若是你有更好的方式實現一樣的效果,還請不吝賜教...

附加

最後介紹一個強制旋轉屏幕的方法,這個方法能夠在進入某個視圖時,強制轉成你須要的屏幕方向,用的比較多的是在一個豎屏的應用中強制轉換某一個界面爲橫屏(例如播放視頻):

PS: 這個方法的使用有個前提, 必定是在跟視圖的-(BOOL)shouldAutorotate返回值爲YES的時候纔有效.

//強制旋轉屏幕  
- (void)orientationToPortrait:(UIInterfaceOrientation)orientation {  
    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];//前兩個參數已被target和selector佔用  
    [invocation invoke];  
      
}

使用的時候,只須要把你須要旋轉的方向傳過去便可!
有一點須要注意:從A進入B的時候, 把B強制轉換成橫屏,返回的時候,須要在A出現的時候再轉換爲原來的方向,否則會有問題;我的建議能夠在BviewWillAppear調用這個方法,轉換屏幕(例如轉換爲橫屏),而後在AviewWillAppear中轉換回來; 若是使用通知來告訴跟視圖是否能夠自動旋轉, 這個方法要在通知發送以後調用.

具體效果及設置, 可參考demo: LZInterfaceRotation

(完)

相關文章
相關標籤/搜索