UI篇-關於單個頁面螢幕旋轉要注意的問題

weixin_34019929發表於2017-02-08
1755386-7ac8f6ff8449535f.png

前言

有時候,我們會需要在整個專案中,使某一個ViewController支援螢幕旋轉,而其他的ViewController並不能自動旋轉。這是一個很常見的需求,下面就螢幕旋轉相關問題做個小結。


強制頁面旋轉(假的螢幕旋轉)

最多見的是,視屏播放中的橫屏模式,點選全屏按鈕,播放頁面橫屏最大化。使用
CGAffineTransformMakeRotation旋轉操作配合動畫即可。

-(void)fullScreenClick: (UIButton *)btn {
    btn.selected = !btn.selected;   
    if (btn.selected) {
    self.mySuperView = self.superview;
    [UIView animateWithDuration:0.3 animations:^{
        [[UIApplication sharedApplication].keyWindow addSubview:self];
        self.transform = CGAffineTransformMakeRotation(M_PI / 2);
    } completion:nil];
    self.frame = self.bigFrame;

}else {
    [self removeFromSuperview];
    [self.mySuperView addSubview:self];
    [UIView animateWithDuration:0.3 animations:^{
        self.transform = CGAffineTransformMakeRotation(M_PI * 2);
    } completion:nil];
    self.frame = self.smallFrame;
  }  
}

強制帶有導航條的整個頁面旋轉

1755386-1cfe9225b506a6de.gif
- (void)changeScreent :(BOOL)toRight
{
    CGFloat duration = [UIApplication sharedApplication].statusBarOrientationAnimationDuration;
    [UIView animateWithDuration:duration animations:^{
        // 修改狀態列的方向及view的方向進而強制旋轉螢幕
        [[UIApplication sharedApplication] setStatusBarHidden:toRight];
        self.navigationController.view.transform = toRight?CGAffineTransformMakeRotation(M_PI_2): CGAffineTransformIdentity;
        if (toRight) {
            self.navigationController.view.bounds = CGRectMake(self.navigationController.view.bounds.origin.x, self.navigationController.view.bounds.origin.y, [UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width);
        }else{
            self.navigationController.view.bounds = CGRectMake(self.navigationController.view.bounds.origin.x, self.navigationController.view.bounds.origin.y, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
        }   
    }];
    
}

注意狀態列無法強制旋轉,在手機方向不變的情況下,所以,最好把狀態列隱藏掉,回覆的時候再顯示出來。不可使用self.view.frame.size.width self.view.frame.size.height,這樣會出現第一次旋轉出現上下部分白邊的Bug,需要使用 [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height。

自動螢幕旋轉

系統支援橫屏的順序

系統支援橫屏順序為以下幾種,前面的會使後面的方法失效,優先順序依次降低。

  • 預設讀取plist裡面設定的方向(優先順序最高)等同於Xcode Geneal設定裡面勾選
1755386-75e493483eba7ad4.png
Paste_Image.png
  • application window設定的級別次之

    application支援所有
    - (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
    return UIInterfaceOrientationMaskAll;//支援所有方向
    }
    
  • UINavigationcontroller裡設定

     - (BOOL)shouldAutorotate//是否支援旋轉螢幕{
      return YES;
    }
    - (NSUInteger)supportedInterfaceOrientations//支援哪些方向{
      return UIInterfaceOrientationMaskPortrait;
    }
    
  • 級別最低的是viewcontroller裡的設定

如何實現某一個頁面螢幕旋轉,而其他頁面不旋轉

首先, - (BOOL)shouldAutorotate 必須在 self.window.rootViewController 中才能有效果,而且每當手機發生旋轉時,就會掉用 rootViewController 的 - (BOOL)shouldAutorotate 方法。

首先我們要保證工程設定為未勾選的狀態才行。

1755386-fc5ba5c440a662c8.png
Paste_Image.png

一般我們的rootViewController 都是UINavigationcontroller ,所有我們在UINavigationcontroller中設定如下方法
#獲取棧最頂端的controller對旋轉的支援狀態即可,
#然後在每一個VC中都要設定 - (BOOL)shouldAutorotate 來確定當前的VC是否支援橫豎屏
# 如果支援,還需要設定 - (NSUInteger)supportedInterfaceOrientations//支援哪些方向

- (BOOL)shouldAutorotate
{
   return  self.topViewController.shouldAutorotate;
}

如果rootViewController 都是 tarBarController ,所有我們在tarBarController中設定如下方法

- (BOOL)shouldAutorotate{

   return  self.selectedViewController.shouldAutorotate;

}

這樣的情況下,每個VC都是預設支援旋轉的,那麼我們需要在每個VC中都設定- (BOOL)shouldAutorotate嗎?答案是否定的。我們可以建立一個controller的基類BaseViewController每個controller都繼承BaseViewController ,在BaseViewController中重寫- (BOOL)shouldAutorotate 方法中 return NO; 預設關閉,,在需要開啟的子類中再次重寫- (BOOL)shouldAutorotate 方法,在方法中return YES即可。

下面是一個支援旋轉螢幕VC的程式碼
- (BOOL)shouldAutorotate
 {
      return  YES;
 }
 # 點選全屏
 - (IBAction)large:(id)sender
{
    NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
}
 # 全屏返回
- (IBAction)largeBack:(id)sender
{
    NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
}
# 在這個方法中設定螢幕旋轉時的 頁面設定。
- (void)willRotateToInterfaceOrientation:    (UIInterfaceOrientation)toInterfaceOrientation
                                duration:(NSTimeInterval)duration
{
    self.navigationController.navigationBarHidden = NO;
    self.toolBar.hidden = NO;
    self.largeBackButton.hidden = YES;
    self.bottomView.hidden = NO;
    self.largeTitleLabel.hidden = YES;
    self.localRecrodContraint.constant = 10;
    if(toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
       toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)
    {
        self.navigationController.navigationBarHidden = YES;
        self.localRecrodContraint.constant = 50;
        self.toolBar.hidden = YES;
        self.largeTitleLabel.hidden = NO;
        self.largeBackButton.hidden = NO;
        self.bottomView.hidden = YES;
    }
}

******************更新****************

上面的方法完美解決了我一個工程的單頁面螢幕旋轉問題,可是在另一個工程中,上面的方法確出現了一個Bug,真的很蛋疼。

按照上面的方法我確實達到了,單頁面旋轉,其他頁面不旋轉的效果,但是有個問題: 在頁面不旋轉的情況下,狀態列確會隨著手機的旋轉而旋轉,著實蛋疼。目前不清楚為什麼一樣的設定,在兩個專案中效果不一樣。

解決方法:

 # AppDelegate 中
@property (strong, nonatomic) MyNavigationController* nav;  
 //手機方向發生變化時就會掉用
   手機方向不發生變化時就不會掉用
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
    return self.nav.topViewController.supportedInterfaceOrientations;//支援所有方向
}
 # MyNavigationController中
- (BOOL)shouldAutorotate
{
    return  self.topViewController.shouldAutorotate;   
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return self.topViewController.supportedInterfaceOrientations;
}
 # BaseViewController中
- (BOOL)shouldAutorotate
{
    return NO;
}
 //不設定的話,雖然頁面是沒有旋轉,但是狀態列會隨著手機旋轉而旋轉。
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

 #需要旋轉的VC中
- (BOOL)shouldAutorotate
{
    return YES;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAllButUpsideDown;
}

可見,狀態列的方向跟頁面支援的方向是一樣的。所以頁面支援的方向是一定要設定的,不設定的話就是預設的:左中右。

螢幕旋轉中的其它問題
  • 如何應用程式剛啟動時判斷裝置方向呢?之前說的那些都是都是在rootViewController之後去判斷的,但是,在程式剛剛啟動時做這些判斷都是無效的。下面是網上的一個方法(未驗證),在didFinishLaunchingWithOptions函式中:

    //註冊通知
    UIDevice *device = [UIDevice currentDevice];
    [device beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenterdefaultCenter] addObserver: self
             selector: @selector(deviceOrientationDidChangeAction:)
                                               name: UIDeviceOrientationDidChangeNotification
                                             object: nil];  
    
    [device endGeneratingDeviceOrientationNotifications];
     //轉屏處理函式:
    
    - (void) deviceOrientationDidChangeAction:(NSNotification *)note
    
    {
    
       NSInteger currentOrientation = [[note object] orientation];
    
       switch (currentOrientation)  {
              case0: {   //未知方向
                 break;
          }
          case1: {   //home鍵向下
                break;
          }
          case2: {   //home鍵向上
                break;
          }
          case3: {  //home鍵向左
                break;
          }
          case4: {  //home鍵向右
                 break;
          }
          default:
                break;
      }
    }
    
    還要在恰當的時候移除通知  不然會被反覆呼叫:
    
    [[NSNotificationCenterdefaultCenter]   removeObserver:self  name:UIDeviceOrientationDidChangeNotification   object:nil];
    
  • 螢幕旋轉時,狀態列會預設隱藏的,如何顯示出來

    //iOS8 橫屏的時候系統預設隱藏了
    [UIApplication sharedApplication].statusBarHidden = YES;
    [UIApplication sharedApplication].statusBarHidden = NO;
    
     # 請注意,上面的倆條一條都不可以少,而且也不可以顛倒順序。我看著也醉了。但是卻是有作用。
    

小結

關於螢幕旋轉的問題,目前先寫這些,後續如果有新的東西收穫,會更新上去的。

相關文章