iOS屏幕旋转
一、UIDeviceOrientation 与 UIInterfaceOrientation的区别
UIDeviceOrientation 是机器硬件的当前旋转方向,这个枚举只能取值,不能手动设置,通过UIDeviceOrientationDidChangeNotification可以监听其方向的改变。网上有屌丝用以下方式进行修改,在这里不建议这样做
NSNumber *value = [NSNumber numberWithInt:UIDeviceOrientationPortrait];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
UIDeviceOrientation枚举值如下:
typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
UIDeviceOrientationUnknown,
UIDeviceOrientationPortrait, // Device oriented vertically, home button on the bottom
UIDeviceOrientationPortraitUpsideDown, // Device oriented vertically, home button on the top
UIDeviceOrientationLandscapeLeft, // Device oriented horizontally, home button on the right
UIDeviceOrientationLandscapeRight, // Device oriented horizontally, home button on the left
UIDeviceOrientationFaceUp, // Device oriented flat, face up
UIDeviceOrientationFaceDown // Device oriented flat, face down
}
UIInterfaceOrientation 是程序界面当前旋转方向,通过UIApplicationDidChangeStatusBarOrientationNotification可以监听到即界面的布局方向发生了改变。(只有布局改变了才能收到通知)并且这个枚举是可以手动设置的。(可以理解为屏幕相对的home键方向)
UIInterfaceOrientation枚举值如下:
// Note that UIInterfaceOrientationLandscapeLeft is equal to UIDeviceOrientationLandscapeRight (and vice versa).
// This is because rotating the device to the left requires rotating the content to the right.
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown,
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait,
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
}
二、如果你的App不支持屏幕旋转,有以下两种方式处理
- 设置AppDelegate里的一下方法,返回值即为屏幕固定方向
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return UIInterfaceOrientationMaskLandscapeRight;
}
- General-Deployment Info-Device Orientation对支持的方向进行勾选
两种方式都可以触发UIDeviceOrientationDidChangeNotification通知
三、如果你的App需要屏幕自适应重力感应进行旋转,并且需要针对每个VC进行单独控制
当然这种情况你完全可以手动地对每个控制器的view布局,在监听到UIDeviceOrientationDidChangeNotification通知后做旋转处理。一两个界面还好,如果控制多的话,还是需要按一下方式处理
- General-Deployment Info-Device Orientation勾选所有支持的方向,代码设置的支持旋转方向都会在此基础上
- 对项目中导航控制器的父控制器增加方法,所有想说的都在代码里
// 是否支持屏幕旋转
- (BOOL)shouldAutorotate
{
return self.topViewController.shouldAutorotate;
}
/**
适用于Push进去的子类调用
@return 屏幕支持的旋转方向
*/
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return self.topViewController.supportedInterfaceOrientations;
}
/**
适用于modal进去的子类调用
@return 屏幕支持的旋转方向
*/
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return self.topViewController.preferredInterfaceOrientationForPresentation;
}
- 在push的控制器中,如果想关闭屏幕旋转,做如下操作
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
// 设置屏幕旋转方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
// 设置是否支持屏幕旋转
-(BOOL)shouldAutorotate
{
return NO;
}
- 如果想在另一个push的控制器再开启屏幕旋转
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return (toInterfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
// 设置屏幕旋转方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
// 设置是否支持屏幕旋转
-(BOOL)shouldAutorotate
{
return YES;
}
- 如果想在modal的控制器中,关闭屏幕旋转
// 设置屏幕是否支持旋转
- (BOOL)shouldAutorotate
{
return NO;
}
// 设置屏幕方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscapeLeft;
}
四、遇到的问题
自己写了一个视频录制的控制器,本来是通过UIDeviceOrientationDidChangeNotification监听手机旋转方向来对视频方向进行调整,实现视频的横竖屏录制(视频的方向枚举AVCaptureVideoOrientation,有兴趣的童鞋可以了解下)
typedef NS_ENUM(NSInteger, AVCaptureVideoOrientation) {
AVCaptureVideoOrientationPortrait = 1,
AVCaptureVideoOrientationPortraitUpsideDown = 2,
AVCaptureVideoOrientationLandscapeRight = 3,
AVCaptureVideoOrientationLandscapeLeft = 4,
}
但是如果系统禁止了屏幕旋转功能,通知就监听不到旋转了,原因是因为此时系统会默认你当前的屏幕状态一直都是你锁屏时的状态。
解决方法
目前大多数用户的苹果手机基本都有螺旋仪和加速器,可以根据这个东西来判断,这时候需要引入CoreMotion.framework这个框架。
- 初始化一个螺旋仪对象
// 系统禁止屏幕旋转后,可以监听到屏幕方向
// 螺旋仪
@property (nonatomic, strong) CMMotionManager *motionManager;
- (void)initialMotionManager
{
if (_motionManager == nil)
{
_motionManager = [[CMMotionManager alloc] init];
}
// 提供设备运动数据到指定的时间间隔(轮询间隔)
_motionManager.deviceMotionUpdateInterval = 0.3;
// 确定"是否使用任何可用的态度参考帧来决定设备的运动"是否可用?
if (_motionManager.deviceMotionAvailable)
{
// 启动设备的运动更新,通过给定的队列向给定的处理程序提供数据
[_motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
// 调用处理函数
[self performSelectorOnMainThread:@selector(handleDeviceMotion:) withObject:motion waitUntilDone:YES];
}];
} else {
self.motionManager = nil;
}
}
- 根据重力感应来判断目前屏幕方向并可以做对应处理
- (void)handleDeviceMotion:(CMDeviceMotion *)deviceMotion
{
double x = deviceMotion.gravity.x;
double y = deviceMotion.gravity.y;
// 竖屏
if (fabs(y) >= fabs(x))
{
// 这个地方注意
// if (y >= 0)
// {
// // UIDeviceOrientationPortraitUpsideDown;
// } else {
// // UIDeviceOrientationPortrait;
// }
self.recordOrientation = UIDeviceOrientationPortrait;
} else {
if (x >= 0)
{
self.recordOrientation = UIDeviceOrientationLandscapeRight;
} else {
self.recordOrientation = UIDeviceOrientationLandscapeLeft;
}
}
}
这里因为我本身的项目需求,只是全局记录下了屏幕当前方向,然后KVO监听recordOrientation,再对视频录制写入方向进行处理。
- 注意一定要在控制器消失前,关闭轮询。否则会造成内存泄漏
// 关闭螺旋仪
[self.motionManager stopDeviceMotionUpdates];
至此,所有我要说的都表达完了。码农共勉!