让某个特定的viewController支持某个特定的旋转方向

#前言:

项目需求:整个app都是只支持竖屏的,但当手机转为横屏模式时,需要加入另外一个横屏的视图,并且在手机转为竖屏时,应该收起当前横屏的视图并显示之前竖屏的那个界面。(类似播放视频的,当横屏时自动转为全屏的横屏播放,当转为竖屏时就会回到竖屏状态)

参考链接:
RAYWENDERLICH
官方文档
Tomson Xu
iOS 关于屏幕旋转shouldAutorotate
里脊串的开发随笔
iOS界面布局

  • iOS6开始rotation变了。
    UIViewController的shouldAutorotateToInterfaceOrientation方法被deprecated。使用supportedInterfaceOrientations and shouldAutorotate 2个方法来代替shouldAutorotateToInterfaceOrientation。
    在iOS4和5中,如果没有重写shouldAutorotateToInterfaceOrientation,默认是只支持portrait,不能旋转。
    在iOS6中,如果没有重写supportedInterfaceOrientations and shouldAutorotate,默认是可以旋转除了upside down的方向。而iPad是支持所有方向。
  • 例子1:在iOS4、5iPhone设备中,如果想要支持除upside down外的其它方向的旋转则在view controller里写下如下代码:

    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return (interfaceOrientation != UIDeviceOrientationPortraitUpsideDown);
    }
    
  • 例子2: 在iOS6,iPhone设备,若要不能旋转,只能portait,则在view controller里写下如下代码:

    - (BOOL)shouldAutorotate
    {
        return NO;
    }
    
  • 例子3:在iOS6,iPad设备,若要可以旋转,只支持landscape,则在view controller里写下:

    -(NSUInteger)supportedInterfaceOrientations{
        return UIInterfaceOrientationMaskLandscape;
    }
    
    - (BOOL)shouldAutorotate
    {
        return YES;
    }
    

在iOS4,5中,都是由具体的view controller来决定对应的view的orientation。而在iOS 6,则是由top-most controller来决定view的orientation设置。

例如:你的app的rootViewController是navigation controller”nav”,在“nav”中的stack依次为:main view ->sub view ->sub sub view而main view 有一个button会present modal view modal view
那么在iOS4、5,在iPad里,如果你要实现上述view都仅支持横屏orientation,你需要在上面的main view,sub view,sub sub view,model view都添加下面的代码:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||             interfaceOrientation==UIInterfaceOrientationLandscapeRight);
}

而对于iOS6,由于是由top-most controller来设置orientation,因此你在main view,sub view,sub sub view,model view里添加下面的代码时没有效果的,而应该添加在nav controller里。而modal view则不在nav controller中,因此在modal view中也需要添加下面的代码。

-(NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscape;
}

- (BOOL)shouldAutorotate
{
    return YES;
}

Important

  1. 你需要自定义一个UINavigationController的子类,然后在里面添加上述代码。
  2. 和navigation controller类似,tab controller里的各个view的orientation设置应该放在tab controller里

问题

因为旋转屏幕支持的方向是由top-most controller决定的,导致这样一个问题:在 top-most controller里的views无法拥有不相同的orientation设置。例如在nav controller中,你希望sub sub view是能横能竖的,当希望其他的view只能竖屏,那么在iOS4、5中通过在sub sub view的shouldAutorotateToInterfaceOrientation设置打竖屏和横屏都行,其他的设置只能打竖屏。但在iOS6中则无法实现这种效果,因为只有在top-most controller里设置才有效。
于是你会在nav controller里控制哪个view需要竖屏哪个view需要横屏。

-(NSUInteger)supportedInterfaceOrientations{
    if([[self topViewController] isKindOfClass:[SubSubView class]])
        return UIInterfaceOrientationMaskAllButUpsideDown;
    else
        return UIInterfaceOrientationMaskPortrait;
}

但这样子会出现一个问题:当你在sub sub view打横屏,然后back to sub view,这时的sub view也变成横屏了。

解决方案:

在tabbarcontroller中写如下代码:

        /**
        *  重写旋转控制的相关代码,因为在iOS6以后,设置orientation是由top-most controller来的,所以你需要在navigationController中添加下面的代码,但modal view是不属于nav controller中的,所以需要单独对modalview添加下面的代码
        */

    - (BOOL)shouldAutorotate{

    NSInteger index = self.selectedIndex;
    //
    UINavigationController *selectedNavController = (UINavigationController *)[self.viewControllers objectAtIndex:index];
    //



    UIViewController *topViewController = [selectedNavController.viewControllers objectAtIndex:0];
    NSLog(@"\n%s~~~visibleVCclass:%@",__PRETTY_FUNCTION__,NSStringFromClass([selectedNavController.visibleViewController class]));
    return selectedNavController.visibleViewController.shouldAutorotate;

    return topViewController.shouldAutorotate;
}

- (NSUInteger)supportedInterfaceOrientations{


    NSLog(@"\n%s~~~visibleVCclass:%@",__PRETTY_FUNCTION__,NSStringFromClass([self.selectedViewController class]));
    return self.selectedViewController.supportedInterfaceOrientations;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
    NSLog(@"\n%s~~~visibleVCclass:%@",__PRETTY_FUNCTION__,NSStringFromClass([self.selectedViewController class]));

    return self.selectedViewController.preferredInterfaceOrientationForPresentation;
}
  • 在navigationController中:

    #pragma mark - 重写navigationController旋转代码
    - (BOOL)shouldAutorotate{
    
        NSLog(@"\n%s~~~visibleVCclass:%@",__PRETTY_FUNCTION__,NSStringFromClass([self.visibleViewController class]));
        return self.visibleViewController.shouldAutorotate;
        return self.topViewController.shouldAutorotate;
    
    }
    
    - (NSUInteger)supportedInterfaceOrientations{
    
        NSLog(@"\n%s~~~visibleVCclass:%@",__PRETTY_FUNCTION__,NSStringFromClass([self.visibleViewController class]));
        return self.visibleViewController.supportedInterfaceOrientations;
    
    }
    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
        NSLog(@"\n%s~~~visibleVCclass:%@",__PRETTY_FUNCTION__,NSStringFromClass([self.visibleViewController class]));
        return self.visibleViewController.preferredInterfaceOrientationForPresentation;
    }
    
  • 在sub viewcontroller中:

        -(void)viewWillAppear:(BOOL)animated{
        [super viewWillAppear:animated];
    
        [self switchToPortrait];
        }
    #pragma mark - 旋转
    //支持的方向
    -(NSUInteger)supportedInterfaceOrientations
    {
        NSLog(@"%s",__PRETTY_FUNCTION__);
        return UIInterfaceOrientationMaskPortrait;
    }
    //是否支持屏幕旋转
    -(BOOL)shouldAutorotate
    {
        NSLog(@"%s",__PRETTY_FUNCTION__);
        return YES;
    }
    -(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
        NSLog(@"%s",__PRETTY_FUNCTION__);
        return UIInterfaceOrientationPortrait;
    }
    /**
    *  强制转为横屏
    */
    - (void)switchToPortrait{
    [[UIDevice currentDevice] setValue:[NSNumber             numberWithInteger:UIDeviceOrientationPortrait] forKey:@"orientation"];//这句话是防止手动先把设备置为横屏,导致下面的语句失效.
        [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIDeviceOrientationPortrait] forKey:@"orientation"];
    

    }

  • 在sub sub viewcontroller中:

    #pragma mark - 旋转
    -(BOOL)shouldAutorotate{
        return YES;
    }
    
    -(NSUInteger)supportedInterfaceOrientations{
    
        return UIInterfaceOrientationMaskLandscape;
    
    }
    

    (以上是通过在sub view中push了sub sub view)

如果只是使用navigationcontroller

#pragma mark - 重写navigationController旋转代码
- (BOOL)shouldAutorotate{

    NSLog(@"\n%s~~~visibleVCclass:%@",__PRETTY_FUNCTION__,NSStringFromClass([self.visibleViewController class]));
    return self.visibleViewController.shouldAutorotate;
    return self.topViewController.shouldAutorotate;

}

- (NSUInteger)supportedInterfaceOrientations{

    NSLog(@"\n%s~~~visibleVCclass:%@",__PRETTY_FUNCTION__,NSStringFromClass([self.visibleViewController class]));
    return self.visibleViewController.supportedInterfaceOrientations;

}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
    NSLog(@"\n%s~~~visibleVCclass:%@",__PRETTY_FUNCTION__,NSStringFromClass([self.visibleViewController class]));
    return self.visibleViewController.preferredInterfaceOrientationForPresentation;
}

在sub viewcontroller中:

#pragma mark - 旋转
//支持的方向
-(NSUInteger)supportedInterfaceOrientations
{
    NSLog(@"%s",__PRETTY_FUNCTION__);
    return UIInterfaceOrientationMaskPortrait;
}
//是否支持屏幕旋转
-(BOOL)shouldAutorotate
{
    NSLog(@"%s",__PRETTY_FUNCTION__);
    return YES;
}
-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
    NSLog(@"%s",__PRETTY_FUNCTION__);
    return UIInterfaceOrientationPortrait;
}

在sub sub view中

#pragma mark - 旋转
-(BOOL)shouldAutorotate{
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations{

    return UIInterfaceOrientationMaskLandscape;

}   

关键的还是强制旋转屏幕的那条语句。

- (void)switchToPortrait{
      [[UIDevice currentDevice] setValue:[NSNumber            numberWithInteger:UIDeviceOrientationPortrait] forKey:@"orientation"];//这句话是防止手动先把设备置为横屏,导致下面的语句失效.
          [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIDeviceOrientationPortrait] forKey:@"orientation"];

在iOS6中,当view controller present时,不会调用 willRotateToInterfaceOrientation:duration:,willAnimateRotationToInterfaceOrientation:duration:,didRotateFromInterfaceOrientation:只有在发生rotate时才会调用。

Zerlz wechat
扫码关注一个很懒的程序员!
Winter is coming, give me a penny!