iOS屏幕旋轉---玩起來

iOS屏幕旋轉一直是一個清晰又模糊的概念,看似簡單,可是對接過程當中總會遇到這樣那樣的問題,好比屏幕閃爍、旋轉不生效、狀態欄消失等等一系列使人噁心的bug。筆者負責的項目,屬於Reactnative&&ObjectC混合開發模式,目前的須要須要主app默認豎向展現,同時提供給Reactnative一個支持自動橫豎屏的ViewController。特此針對iOS屏幕旋轉進行了整理概括,望對各位開發者有所幫助。小程序

1、使人噁心的三種枚舉解析

一、設備方向(物理旋轉) - UIDeviceOrientation

  • 硬件設備(ipad、iPhone)背身的旋轉方向,以Home爲參照物
//Portrait 表示縱向,Landscape 表示橫向。
typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
     UIDeviceOrientationUnknown,

     // Device oriented vertically, home button on the top
     UIDeviceOrientationPortraitUpsideDown, 

     // Device oriented horizontally, home button on the right
     UIDeviceOrientationLandscapeLeft,      

    // Device oriented horizontally, home button on the left
     UIDeviceOrientationLandscapeRight,     

    // Device oriented flat, face up
    UIDeviceOrientationFaceUp,             

    // Device oriented flat, face down
    UIDeviceOrientationFaceDown            
} __TVOS_PROHIBITED;
複製代碼
  • 設備的旋轉方向只讀不可寫
獲取當前屏幕的方法:[UIDevice currentDevice].orientation
複製代碼
  • 設備旋轉監聽
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onDeviceOrientationDidChange)
                     name:UIDeviceOrientationDidChangeNotification
                                               object:nil];

[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];


監聽方法實現

 - (BOOL)onDeviceOrientationDidChange{
    //獲取當前設備Device
    UIDevice *device = [UIDevice currentDevice] ;
    //識別當前設備的旋轉方向
    switch (device.orientation) {
        case UIDeviceOrientationFaceUp:
            NSLog(@"屏幕幕朝上平躺");
            break;

        case UIDeviceOrientationFaceDown:
            NSLog(@"屏幕朝下平躺");
            break;

        case UIDeviceOrientationUnknown:
            //系統當前沒法識別設備朝向,多是傾斜
            NSLog(@"未知方向");
            break;

        case UIDeviceOrientationLandscapeLeft:
            NSLog(@"屏幕向左橫置");
            break;

        case UIDeviceOrientationLandscapeRight:
            NSLog(@"屏幕向右橫置");
            break;

        case UIDeviceOrientationPortrait:
            NSLog(@"屏幕直立");
            break;

        case UIDeviceOrientationPortraitUpsideDown:
            NSLog(@"屏幕直立,上下顛倒");
            break;

        default:
            NSLog(@"沒法識別");
            break;
    }
    return YES;
}
複製代碼

二、頁面旋轉(視圖旋轉)- UIInterfaceOrientation

  • UIInterfaceOrientation程序界面的當前旋轉方向(能夠設置)
// Note that UIInterfaceOrientationLandscapeLeft is equal to UIDeviceOrientationLandscapeRight (and vice versa).
// This is because rotating the device to the left requires rotating the content to the right.
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {

     UIInterfaceOrientationUnknown               = UIDeviceOrientationUnknown,

     UIInterfaceOrientationPortrait              = UIDeviceOrientationPortrait,

     UIInterfaceOrientationPortraitUpsideDown    = UIDeviceOrientationPortraitUpsideDown,

     UIInterfaceOrientationLandscapeLeft         = UIDeviceOrientationLandscapeRight,

     UIInterfaceOrientationLandscapeRight        = UIDeviceOrientationLandscapeLeft

    } __TVOS_PROHIBITED;
複製代碼
  • 程序界面的方向使用UIInterfaceOrientation,與設備旋轉方向沒有任何關係
爲了達到頁面的流暢效果
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight, 
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
複製代碼

三、頁面旋轉聚合(iOS6 後出現的)- UIInterfaceOrientationMask

支持多種頁面旋轉方向bash

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),

} __TVOS_PROHIBITED;

複製代碼

2、單頁面旋轉方向設置

  • 實現方法
//方法1
- (BOOL)shouldAutorotate NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;
//方法2
- (UIInterfaceOrientationMask)supportedInterfaceOrientations NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;
// Returns interface orientation masks.
//方法3
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;!
複製代碼
  • 方法解析
方法1:頁面是否支持自動旋轉
方法2:頁面支持的旋轉方向有哪些
方法3:頁面優先展現的旋轉方向
複製代碼

3、兩種屏幕旋轉的觸發方式

一、系統未關閉屏幕自動旋轉功能

//1.決定當前界面是否開啓自動轉屏,若是返回NO,後面兩個方法也不會被調用,只是會支持默認的方向
- (BOOL)shouldAutorotate {
      return YES;
}

//2.返回支持的旋轉方向
//iPad設備上,默認返回值UIInterfaceOrientationMaskAllButUpSideDwon
//iPad設備上,默認返回值是UIInterfaceOrientationMaskAll
- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
     return UIInterfaceOrientationMaskAll;
}

//3.返回進入界面默認顯示方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
     return UIInterfaceOrientationPortrait;
}
複製代碼

二、系統關閉屏幕自動旋轉功能

在程序界面經過點擊等方式切換到橫屏微信

// 方法1:
- (void)setInterfaceOrientation:(UIDeviceOrientation)orientation {
      if ([[UIDevice currentDevice]   respondsToSelector:@selector(setOrientation:)]) {
          [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:orientation]     
                                       forKey:@"orientation"];
        }
    }

//方法2:
- (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];
        }
    }
複製代碼
  • 確保shouldAutorotate方法返回YES
  • 二者使用的參數類型不

4、屏幕旋轉控制的優先級

一、屏幕旋轉設置方式

- Xcode的General設置
- Xcode的nfo.plist設置
- 代碼設置Appdelegete中
複製代碼

二、旋轉優先級

工程Target屬性配置(全局權限) > Appdelegate&&Window > 根視圖控制器> 普通視圖控制器
複製代碼

三、開啓App旋轉的全局權限

  • Device Orientation屬性配置
【General】—>【Deployment Info】—>【Device Orientation】
值得注意的是,對於iPhone,若是四個屬性咱們都選或者都不選,效果和默認的狀況同樣  
複製代碼
  • Info.Plist設置
Supported interface orientation
與第一種方式同樣的效果,兩種方式最終都是設置info.plist中的屬性
複製代碼
  • Appdelegate&&Window中設置
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {

    return  UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;

}
複製代碼

若是咱們實現了Appdelegate的這一方法,那麼咱們的App的全局旋轉設置將以這裏的爲準,即便前兩種方法的設置與這裏的不一樣。app

5、設置單頁面的旋轉權限

開發中常涉及到的控制器ide

UITabbarViewController,UINavigationBarController ,UIViewController
複製代碼

咱們的項目都是用UITabbarViewController做爲Window的根視圖控制器,而後管理着若干個導航控制器UINavigationBarController,再由導航欄控制器去管理普通的視圖控制器UIViewController。若以此爲例的話,關於旋轉的優先級從高到低就是UITabbarViewController>UINavigationBarController >UIViewController了。若是具備高優先級的控制器關閉了旋轉設置,那麼低優先級的控制器是沒法作到旋轉的。函數

好比說咱們設置要單個視圖控制器能夠自動旋轉,這須要在視圖控制器中增長shouldAutorotate方法返回YES或者NO來控制。但若是存在上層根視圖控制器,而咱們只在這個視圖控制器中實現方法,會發現這個方法是不走的,由於這個方法被上層根視圖控制器攔截了學習

6、實現自動可控的旋轉

一、逐級實現

  • 一、UITabbarViewController
//是否自動旋轉
-(BOOL)shouldAutorotate{
    return self.selectedViewController.shouldAutorotate;
}

//支持哪些屏幕方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return [self.selectedViewController supportedInterfaceOrientations];
}

//默認方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
    return [self.selectedViewController preferredInterfaceOrientationForPresentation];
}
複製代碼
  • 二、UINavigationController
//是否自動旋轉
//返回導航控制器的頂層視圖控制器的自動旋轉屬性,由於導航控制器是以棧的緣由疊加VC的
//topViewController是其最頂層的視圖控制器,
-(BOOL)shouldAutorotate{
    return self.topViewController.shouldAutorotate;
}

//支持哪些屏幕方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return [self.topViewController supportedInterfaceOrientations];
}

//默認方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
    return [self.topViewController preferredInterfaceOrientationForPresentation];
}
複製代碼

其實就是高優先級的視圖控制器要跟隨低優先級控制器的旋轉配置。這樣就可以達到目的優化

二、使用模態視圖

使用模態視圖能夠不受這種根視圖控制器優先級的限制。這個也很容易理解,模態彈出的視圖控制器是隔離出來的,不受根視圖控制的影響。具體的設置和普通視圖器代碼相同
複製代碼

7、實現需求

App主要主界面豎向展現,部分頁面橫向展現動畫

兩種解決方案ui

一、逐級控制

  • 步驟方式
1.開啓全局權限設置項目支持的旋轉方向
2.自定義標籤控制器和導航控制器來設置屏幕的自動旋轉。
3.自定義基類控制器設置不支持自動轉屏,並默認只支持豎屏
4.對項目中須要轉屏幕的控制器開啓自動轉屏、設置支持的旋轉方向並設置默認方向
複製代碼

二、經過全局監聽當前的方向變化

  • 步驟方式
1.在Applegate文件中增長一個用於記錄當前屏幕是否橫屏的屬性
2.須要橫屏的界面,進入界面後強制橫屏,離開界面時恢復豎屏
複製代碼
  • 核心代碼
一、添加監聽

[[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(onDeviceOrientationDidChange) name:UIDeviceOrientationDidChangeNotification
                                               object:nil];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];

二、實現監聽

- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
    if (_allowAutoRotate) {
        //只支持橫屏
        return UIInterfaceOrientationMaskLandscape;
    }else{
        //支持豎屏
        return UIInterfaceOrientationMaskPortrait;
    }
}

三、頁面控制

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
    delegate.allowAutoRotate = YES;
    //進入界面:設置橫屏
    [self setDeviceInterfaceOrientation:UIDeviceOrientationLandscapeLeft];
}

- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
    delegate.allowAutoRotate = NO;
    //離開界面:設置豎屏
    [self setDeviceInterfaceOrientation:UIDeviceOrientationPortrait];
}
複製代碼

8、優化顯示

視圖切換顯示異常問題

當前viewController達到預期效果,可是在返回上一頁時,或者在當前頁面不不支持的方向的上一頁進來時,不能當即達到預期狀態,須要設備方向更換一次才能恢復正常

#pragma mark -UITabBarControllerDelegate
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
    [self presentViewController:[UIViewController new] animated:NO completion:^{
        [self dismissViewControllerAnimated:NO completion:nil];
    }];
}
#pragma mark -UINavigationControllerDelegate
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    [self presentViewController:[UIViewController new] animated:NO completion:^{
        [self dismissViewControllerAnimated:NO completion:nil];
    }];
}
複製代碼

可是會出現屏幕閃爍問題,不推薦使用此方案,能夠經過強制設置屏幕方向,在視圖的鉤子函數中進行手動控制

屏幕旋轉後,狀態欄沒法顯示

  • 設置info.plist 中 View controller-based status bar appearance YES
  • 對應的控制其中實現下面代碼
//設置樣式
- (UIStatusBarStyle)preferredStatusBarStyle {
    return UIStatusBarStyleLightContent;
}
 
//設置是否隱藏
- (BOOL)prefersStatusBarHidden {
//    [super prefersStatusBarHidden];
    return NO;
}
 
//設置隱藏動畫
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
    return UIStatusBarAnimationNone;
}

複製代碼

原文連接: tech.meicai.cn/detail/83, 也可微信搜索小程序「美菜產品技術團隊」,乾貨滿滿且每週更新,想學習技術的你不要錯過哦。

相關文章
相關標籤/搜索