1. 程式人生 > >IOS橫豎屏

IOS橫豎屏

1.兩種螢幕旋轉的觸發方式

我們開發的App的,大多情況都是大多介面支援豎屏,幾個特別的介面支援旋轉橫屏,兩種介面相互切換,觸發其旋轉有兩種情況:

情況1:系統沒有關閉自動旋轉螢幕功能

//1.決定當前介面是否開啟自動旋轉螢幕,如果返回NO,後面兩個方法也不會被呼叫,只是會支援預設的方向
- (BOOL)shouldAutorotate {
      return YES;
}

//2.返回支援的旋轉方向
//iPad裝置上,預設返回值UIInterfaceOrientationMaskAllButUpSideDwon
//iPad裝置上,預設返回值是UIInterfaceOrientationMaskAll
- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
     return UIInterfaceOrientationMaskAll;
}

//3.返回進入介面預設顯示方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
     return UIInterfaceOrientationPortrait;
}

這種情況,支援旋轉的介面跟隨使用者手持裝置旋轉方向自動旋轉。我們需要在當前檢視控制器中新增如下方法:

情況2:單個介面強制旋轉

在程式介面通過點選等方式切換到橫屏(尤其是視訊播放的情況),有以下兩種方法:

// 方法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,這樣這兩個方法才會生效。還要注意兩者使用的引數型別不同。

 

看到這裡不太明白NSInvocation是在做神馬,去查了一下資料:

一、簡介

  • 在 iOS中可以直接呼叫某個物件的訊息方式有兩種:
    • 一種是 performSelector:withObject;
    • 再一種就是 NSInvocation。
  • 第一種方式比較簡單,能完成簡單的呼叫。但是對於 >2個的引數或者有返回值的處理,那就需要做些額外工作才能搞定。那麼在這種情況下,我們就可以使用NSInvocation來進行這些相對複雜的操作。

二、使用方式

  • 程式碼:
 - (void)viewDidLoad {
    [super viewDidLoad];
    //NSInvocation;用來包裝方法和對應的物件,它可以儲存方法的名稱,對應的物件,對應的引數,
    /*
     NSMethodSignature:簽名:再建立NSMethodSignature的時候,必須傳遞一個簽名物件,簽名物件的作用:用於獲取引數的個數和方法的返回值
     */
    //建立簽名物件的時候不是使用NSMethodSignature這個類建立,而是方法屬於誰就用誰來建立
    NSMethodSignature*signature = [ViewController instanceMethodSignatureForSelector:@selector(sendMessageWithNumber:WithContent:)];
    //1、建立NSInvocation物件
    NSInvocation*invocation = [NSInvocation invocationWithMethodSignature:signature];
    invocation.target = self;
    //invocation中的方法必須和簽名中的方法一致。
    invocation.selector = @selector(sendMessageWithNumber:WithContent:);
    /*第一個引數:需要給指定方法傳遞的值
           第一個引數需要接收一個指標,也就是傳遞值的時候需要傳遞地址*/
    //第二個引數:需要給指定方法的第幾個引數傳值
    NSString*number = @"1111";
    //注意:設定引數的索引時不能從0開始,因為0已經被self佔用,1已經被_cmd佔用
    [invocation setArgument:&number atIndex:2];
    NSString*number2 = @"啊啊啊";
    [invocation setArgument:&number2 atIndex:3];
    //2、呼叫NSInvocation物件的invoke方法
    //只要呼叫invocation的invoke方法,就代表需要執行NSInvocation物件中制定物件的指定方法,並且傳遞指定的引數
    [invocation invoke];
}
 - (void)sendMessageWithNumber:(NSString*)number WithContent:(NSString*)content{
    NSLog(@"電話號%@,內容%@",number,content);
}

 

 

下邊結合自己做專案的過程說一下思路,專案中某幾個頁面需要實現轉屏效果,專案結構是rootViewController為一個UITabbarController,子檢視都是UINavigationController載入的viewcontroller。

首先在專案target->General裡設定Device Orientation為專案支援的所有轉屏型別,我專案勾選了Portrait、left、right三種。

專案中,我只有幾個頁面需要支援轉屏,那麼怎麼確保其他頁面不受影響呢?這裡就要了解一下轉屏的許可權優先順序:

控制螢幕旋轉優先順序為: Appdelegate&&Window > 根檢視控制器> 普通檢視控制器

在一般情況下,我們的專案都是用UITabbarViewController作為Window的根檢視控制器,然後管理著若干個導航控制器UINavigationBarController,再由導航欄控制器去管理普通的檢視控制器UIViewController。若以此為例的話,關於旋轉的優先順序從高到低就是UITabbarViewController>UINavigationBarController >UIViewController了。如果具有高優先順序的控制器關閉了旋轉設定,那麼低優先順序的控制器是無法做到旋轉的。

比如說我們設定要單個檢視控制器可以自動旋轉,這需要在檢視控制器中增加shouldAutorotate方法返回YES或者NO來控制。但如果存在上層根檢視控制器,而我們只在這個檢視控制器中實現方法,會發現這個方法是不走的,因為這個方法被上層根檢視控制器攔截了。理解這個原理後,我們可以這樣實現自動逐級設定各檢視控制器,高優先順序的檢視控制器影響低優先順序控制器,

解決上述的問題我們需要設定BaseTabbarViewController(根檢視控制器繼承該類)如下:它的返回值依賴於當前選中的UINavigationController的值

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

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

//預設方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
    return [self.selectedViewController preferredInterfaceOrientationForPresentation];
}

設定導航控制器BaseNavigationController(所有專案中的navigation繼承該類)如下:它的值依賴於當前展示的頁面返回值

//是否自動旋轉
//返回導航控制器的頂層檢視控制器的自動旋轉屬性,因為導航控制器是以棧的原因疊加VC的
//topViewController是其最頂層的檢視控制器,
-(BOOL)shouldAutorotate{
    return self.topViewController.shouldAutorotate;
}

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

//預設方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
    return [self.topViewController preferredInterfaceOrientationForPresentation];
}

到這裡,我們就應該明白了,其實就是高優先順序的檢視控制器要跟隨低優先順序控制器的旋轉配置。這樣就能夠達到目的。

 

專案中我們要保證其他頁面不支援轉屏,因此在BaseViewController(專案中所有的controller繼承與此)中新增程式碼:

- (BOOL)shouldAutorotate{

    return NO;

}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations{

    return UIInterfaceOrientationMaskPortrait;

}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{

    return UIInterfaceOrientationPortrait;

}

   這樣就所有頁面都不支援轉屏了,只在你想要實現轉屏的controller中重寫這三個方法返回你想要的值就可以了。

 

遇到的坑

但是問題來了,這樣設定過後遇到一個奇怪的問題,就是把手機橫屏啟動程式,介面統統變形了,檢視是以橫屏時候的寬高來佈局的,雖然已經禁用了轉屏(也確實不會轉屏)但初始化構建的介面卻變形了。於是這裡使用了上邊說過的invoke方法強制恢復成了豎屏的樣式,在BaseTabbarViewController和BaseViewController的viewdidload中新增程式碼

- (void)restoreStatusBarOrientation {    

    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 = UIDeviceOrientationPortrait;

            [invocation setArgument:&val atIndex:2];

            [invocation invoke];

    }

}

這樣所有介面無論橫屏豎屏的狀態下開啟都為豎屏的樣式了,最後就可以把精力放到支援轉屏的介面進行開發了。

 

如何適配轉屏的介面呢,通過通知監聽橫豎屏變化再改變你介面的佈局就好了

//螢幕旋轉通知

        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];

        [[NSNotificationCenter defaultCenter] addObserver:self

                                                 selector:@selector(handleDeviceOrientationDidChange:)

                                                     name:UIDeviceOrientationDidChangeNotification

                                                   object:nil

         ];

- (void)handleDeviceOrientationDidChange:(UIInterfaceOrientation)interfaceOrientation{

    UIDevice *device = [UIDevice currentDevice] ;   

        switch (device.orientation) {

        case UIDeviceOrientationFaceUp:

        case UIDeviceOrientationFaceDown:

        case UIDeviceOrientationUnknown:

        case UIDeviceOrientationPortraitUpsideDown:

            break;

        case UIDeviceOrientationLandscapeLeft:

        case UIDeviceOrientationLandscapeRight:

        {

//橫屏

           }

            break;

        case UIDeviceOrientationPortrait:

        {

//豎屏

        }

            break;

        default:

            break;

    }

}

 

我是自定義了一些巨集 來保證各個機型和橫屏豎屏都儘可能自動去返回理想值,這裡就智者見智仁者見仁了。

這篇部落格:https://blog.csdn.net/DreamcoffeeZS/article/details/79037207這裡講得比較全面  可以參考

 

還有一種思路是整個專案並不支援橫豎屏轉動,是通過動畫旋轉指定介面來實現的轉屏效果,這個如果想了解可以去搜索其他文章參考。