iOS开发中屏幕旋转(二)

Morris_ 2019.04.11

前面有总结过一些在开发中遇到的屏幕旋转的基础知识

一、设置应用支持的转屏方向

设置方式

00x1

在TARGET->General->Deployment Info->Device Orientations下,可设置App支持的屏幕旋转方向。

这里的设置会同步到应用的plist文件中。

00x2

苹果也提供了代码设置app支持的转屏方式的Api:

- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
    return UIInterfaceOrientationMaskAllButUpsideDown;
}

这个Api是UIApplicationDelegate中的一个方法,我们在AppDelegate.m中实现此协议方法即可。

区别比较

按照00x1设置后,如果代码中又使用了00x2,则00x2会覆盖00x1的设置。但是对于闪屏的方向设置,00x2的方式是无效的,00x1的设置方式确是有效的。可以理解为,应用在进入时,先读取了plist文件中的屏转设置,然后按照plist文件中的设置进行闪屏的展示,程序执行进AppDelegate.m中时,通过00x2中的Api又获取了一次应用所支持的屏转设置,所以闪屏支持的屏幕旋转设置,必须用00x1的方式设置,00x2的设置是无效的,进入应用后应用支持的转屏方式两种方式都可以。

小结

设置闪屏支持的转屏方式,只能使用00x1的方式,00x2的方式是无效的。

如果不使用代码,直接使用00x1的方式,是可行的。

如果既使用00x1的方式,也使用了代码设置,则00x1的方式设置了闪屏支持的旋转方向,00x2的方式设置了进入应用后界面支持的旋转方向。当然个别界面可以单独设置其支持的旋转方向。

二、设置某个界面支持的转屏方向

有必要了解一下苹果提供的UIViewController的分类UIViewControllerRotation。

// To make it more convenient for applications to adopt rotation, a view controller may implement the below methods. Your UIWindow's frame should use [UIScreen mainScreen].bounds as its frame.
@interface UIViewController (UIViewControllerRotation)

...略

- (BOOL)shouldAutorotate NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;
- (UIInterfaceOrientationMask)supportedInterfaceOrientations NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;
// Returns interface orientation masks.
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;

...略

@end

需要注意一下我们经常用到的设置某个controller支持的屏转方向的三个方法。

- (BOOL)shouldAutorotate
{
    return YES;//设置当前controller是否支持重力感应,即是否可以转屏
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAllButUpsideDown;//设置支持的转屏方向
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return UIInterfaceOrientationPortrait;//设置初始的屏幕方向
}

iOS开关中屏幕旋转(一)中,有说道present出来的视图和push出来的视图的转屏设置有些区别的,在这里重新说明一下,实际上在本质上是没有什么区别的。
push出来的视图的转屏方式,不是看push出来的controller里的设置,而是由controller所在的navigationController所决定的,navigationController是继承自UIViewController的。本质上,不管push出来多少个视图控制器,显示还是在navigationController下的,所以push出来的视图控制器的转屏方式取决于他的navigationController。

navigationController下的子控制器的转屏方向设置不取决于自己,取决于当前所在的navigationController,因为navigationController是继承于UIViewController的,所以在navigationController里进行转屏设置也是有效的。

为了方便不同界面的转屏设置,我们可以在BaseViewController中添加一个属性orientation,系统的UIViewController有一个属性interfaceOrientation,但是是只读的,不能设置。

@property(nonatomic,readonly) UIInterfaceOrientation interfaceOrientation NS_DEPRECATED_IOS(2_0,8_0) __TVOS_PROHIBITED;

我们可以复写这个属性或者重新写一个UIInterfaceOrientation类型的属性。

@interface BaseViewController : UIViewController

@property (nonatomic) UIInterfaceOrientation interfaceOrientation;

@end

设置初始默认值,并设置默认不支持转屏,在需要支持转屏的界面单独设置,也可以添加一个是否支持转屏的BOOL类型的属性。

@implementation BaseViewController

- (instancetype)init {
    if (self = [super init]) {
        _interfaceOrientation = UIInterfaceOrientationPortrait;
    }
    return self;
}

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        _interfaceOrientation = UIInterfaceOrientationPortrait;
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
}


- (BOOL)shouldAutorotate {
    return NO;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskAllButUpsideDown;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return _interfaceOrientation;
}

@end

那么有个问题,有navigationController下的控制器,这样设置是无效的,因为navi下的控制器的转屏取决于当前的nav,而不是子控制器。如果在navigationController下有A和B两个controller,A不支持旋转,B需要支持旋转,这怎么处理?在进入A页面的时候设置navi的转屏方式为不可旋转,进入B页面的时候设置为可旋转,原则上这样可以解决这个需求。那么是不是也要给navi写一个类似的属性呢,给navi也写一个这样的属性来控制问题能解决,但是这样就很麻烦。

有没有什么更简单使用的方法呢?

给UIViewController扩展一个设置屏幕旋转方向的属性orientation和一个是否支持屏幕旋转的属性, 注意尽量不要和系统的属性重名,以免覆盖系统提供的功能。

@interface UIViewController (Rotation)

//是否支持重力感应 注意不要和系统的shouldAutorotate属性重名,以免覆盖系统提供的功能
@property (nonatomic) BOOL autoRotate;

//方向 注意尽量不要和系统的interfaceOrientation属性重名,以免覆盖系统提供的功能
@property (nonatomic) UIInterfaceOrientation orientation;

@end
#import "UIViewController+Rotation.h"
#import <objc/runtime.h>

static NSString const *kUIViewControllerRotationKey = @"UIViewControllerRotationKey";
static NSString const *kUIViewControllerAutoRotateKey = @"UIViewControllerAutoRotateKey";

//为了和系统的分类UIViewController (UIViewControllerRotation)冲突,分类命名为Rotation
@implementation UIViewController (Rotation)

- (void)setOrientation:(UIInterfaceOrientation)orientation {
    NSNumber *number = [NSNumber numberWithInteger:orientation];
    objc_setAssociatedObject(self, &kUIViewControllerRotationKey, number, OBJC_ASSOCIATION_RETAIN);//注意这里不能用OBJC_ASSOCIATION_ASSIGN
}
- (UIInterfaceOrientation)orientation {
    NSNumber *number = objc_getAssociatedObject(self, &kUIViewControllerRotationKey);
    if (number) {
        return [number integerValue];
    }
    return UIInterfaceOrientationUnknown;
}

- (void)setAutoRotate:(BOOL)autoRotate {
    NSNumber *number = [NSNumber numberWithBool:autoRotate];
    objc_setAssociatedObject(self, &kUIViewControllerAutoRotateKey, number, OBJC_ASSOCIATION_RETAIN);
}
- (BOOL)autoRotate {
    NSNumber *number = objc_getAssociatedObject(self, &kUIViewControllerAutoRotateKey);
    if (number) {
        return [number boolValue];
    }
    return YES;
}

@end

这样就可以随心所欲的使用这两个属性进行对屏幕旋转的设置了。

- (BOOL)shouldAutorotate {
    return self.autoRotate;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscape;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return self.orientation;
}

二、关于强制转屏的补充

iOS开关中屏幕旋转(一)中国有提到过强转屏。

需要注意的是,只有在支持重力感应的情况下才能使用强制转屏方法,否则是转不了的。有些应用是不支持重力感应,但是支持某些界面用户手动去切换全屏。这种需求就需要我们在强制转屏前将autoRotate属性设置为YES,并同时需要实现支持重力感应的方法,手动切换全屏才会有效,或者使用其他的全屏方法。

- (void)rotateScreen:(UIInterfaceOrientation)orientation
{
    //转屏时,先设置支持重力感应,否则此方法无效
    self.navigationController.autoRotate = YES;
    
    if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)])
    {
        NSNumber *num = [[NSNumber alloc] initWithInt:orientation];
        [[UIDevice currentDevice] performSelector:@selector(setOrientation:) withObject:(id)num];
        [UIViewController attemptRotationToDeviceOrientation];
    }
    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];
    
    self.navigationController.autoRotate = NO;
    
    self.navigationController.orientation = orientation;
}
-(BOOL)shouldAutorotate
{
    return self.autoRotate;
}

如上设置,不支持重力感应,但是支持用户手动切换屏幕旋转,只支持手动全屏的需求时,这样处理。

Demo

iOS开发中屏幕旋转(一)
iOS开发中屏幕旋转(二)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Morris_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值