1. 程式人生 > >iOS螢幕旋轉之為橫屏視訊播放增加豎屏模式

iOS螢幕旋轉之為橫屏視訊播放增加豎屏模式

在App Store上的大部分視訊播放App中,都實現了豎屏小屏播放以顯示更多相關資訊,橫屏切換到全屏播放,這樣的互動顯得優雅而大方。最近專案裡有個這樣的需求,為全屏視訊播放加上豎屏模式。下面,讓我們一起來實現這個需求。

iOS中的方向

iOS裝置中有兩種方向,一種是裝置方向,一種是螢幕檢視方向。我們可以直接呼叫

[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPartrait];

強制更改裝置方向,但該方法已經在iOS6中廢止。另外一種更改裝置方向的方法是通過重力感應觸發。

那麼檢視方向是又什麼決定的呢?

  1. 全域性Info.plist
  2. iOS6+在UIApplicationDelegate中增加了 - (NSUInteger)supportedInterfaceOrientationsForWindow:(UIWindow *)window;回撥
  3. UIViewController。並且只在UIWindow的rootViewController或model狀態下的UIViewController有效

最終的檢視方向取決於 (全域性控制 ∩ UIWindow 中的回撥 ∩ 單個介面控制) 的交集,如果交集為空,iOS6下丟擲 UIApplicationInvalidInterfaceOrientationException 異常後崩潰。

UIController中對檢視方向變化的響應

iOS5:

-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
    return ((toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) |
           (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft));
}
iOS6+:
// 支援轉屏?
(BOOL)shouldAutorotate
{
    return YES;
}
// 支援的螢幕方向
- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscape;
}

實現前後的效果

豎屏: 橫屏:
兩張橫豎屏的圖片中,選單、標題大小都發生了改變。下面我們就來討論下實現的方法。

實現細節

  1. 首先在視訊播放所在UIViewController支援橫豎屏,並在裝置旋轉的回撥中,通知選單當前的裝置方向發生了改變,程式碼如下:
    //裝置旋轉前
    - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
    {
    //橫豎屏變更選單等
    if (toInterfaceOrientation == UIInterfaceOrientationPortrait) {
    //豎屏狀態
    }
     
    //裝置旋轉前
    - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
    {
    //對檢視進行旋轉處理,這裡通過present一個新的UIViewController,暫不需要處理
    }
     
    //裝置旋轉完
    - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
    {
    //裝置旋轉完的處理
    }
    // 支援轉屏
    (BOOL)shouldAutorotate
    {
        return YES;
    }
    // 支援的螢幕方向
    - (NSUInteger)supportedInterfaceOrientations
    {
        return UIDeviceOrientationLandscapeLeft | UIInterfaceOrientationPortrait ;
    }
    -(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
    {
        return ((toInterfaceOrientation == UIInterfaceOrientationPortrait) ||
               (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft));
    }
    willRotateToInterfaceOrientation:duration: 和 willAnimateRotationToInterfaceOrientation:duration:的區別在於呼叫的順序。前者在旋轉前呼叫,並且螢幕方向、裝置原點、檢視大小等都未改變,後者在旋轉的動畫block中呼叫,並且螢幕方向、裝置原點、檢視大小等都已改變。因此在willRotateToInterfaceOrientation:duration:中,應當做變數的更改;在willAnimateRotationToInterfaceOrientation:duration:中,適合做一些重繪工作。
  2. 考慮到原來的視訊播放器只支援橫屏播放,這裡採用的方案如下:豎屏狀態下,先present一個只支援橫屏的UIViewController,再將視訊播放器作為橫屏UIViewController的子控制器新增進來。這樣可以觸發iOS進行橫豎屏檢測。
  3. 當豎屏切到橫屏時,旋轉的動畫設定。程式碼如下:
    [portraitViewController presentViewController:landscapeViewController animated:NO completion:^{
    
                //mvPlayer原先是作為protraitViewController的子UIViewController
                [mvPlayer.view removeFromSuperview]; 
                [mvPlayer removeFromParentViewController];
    
                //改為作為landscapeViewController的子UIViewController
    	    [landscapeViewController addChildViewController:mvPlayer];
                [landscapeViewController.view addSubview:mvPlayer.view]; 
                
                //改變狀態列方向
                [[UIApplication sharedApplication] setStatusBarHidden:YES];
                [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft animated:NO];
                
                //旋轉前
                CGAffineTransform transform = CGAffineTransformMakeRotation(-M_PI_2);
                transform = CGAffineTransformScale(transform, scale, scale);
                mvPlayer.view.transform = transform;
                
                //旋轉動畫
                [UIView animateWithDuration:[[UIApplication sharedApplication] statusBarOrientationAnimationDuration] animations:^{
                    mvPlayer.view.transform = CGAffineTransformIdentity;
                    mvPlayer.view.frame = landscapeViewController.view.bounds;
                }completion:^(BOOL finished) {
                    
                    [[UIApplication sharedApplication] setStatusBarHidden:NO];
                    
                }];
            }];

  4. 橫屏切換到豎屏的動畫實現如下:
    //更改mvPlayer的父UIController
    [mvPlayer.view removeFromSuperview];
    UIViewController *viewController = strongSelf.player.presentingViewController;
    [mvPlayer removeFromParentViewController];
    [viewController dismissModalViewControllerAnimated:NO];
    
    [portraitViewController addChildViewController:mvPlayer];
    [portraitViewController.view addSubview:mvPlayer.view];
            
    //縮放動畫
    CGAffineTransform transform = CGAffineTransformMakeScale(scale, scale);
    mvPlayer.view.transform = transform;
    mvPlayer.view.frame = CGRectMake(0, 0, CGRectGetWidth(mvPlayer.view.frame), CGRectGetHeight(mvPlayer.view.frame));
    mvPlayer.view.transform = CGAffineTransformRotate(transform, M_PI_2);
            
         [UIView animateWithDuration:[[UIApplication sharedApplication] statusBarOrientationAnimationDuration]
                          animations:^{
                              mvPlayer.view.transform = CGAffineTransformMakeScale(scale, scale);
                          }
                          completion:^(BOOL finished) {
                              mvPlayer.view.frame = CGRectMake(0, 0, CGRectGetWidth(mvPlayer.view.frame), CGRectGetHeight(mvPlayer.view.frame));
                                  
                          }];

  5. 以上是手動旋轉過程。還有一種方法是直接旋轉portraitViewController.view。人為旋轉過程中,裝置的原點並未發生改變,這需要考慮橫豎屏佈局問題。

總結

本文介紹橫豎屏切換的一些基礎知識,並實踐了類似騰訊視訊的橫豎屏人為切換方式,達到不同狀態下顯示不同檢視的互動方式。