iOS 获取屏幕锁定状态

在开发中,有时候我们需要让某些特定的页面为横屏展示,而从这个页面离开或者进入其他页面时为竖屏,起初我仅仅依赖了UIViewController的屏幕旋转方法简单的处理,但是当需求慢慢在变化时,我发觉这并不能满足我的需求:

比如导航页需要横屏展示,但是从导航页上面某个按钮跳转的页面需要竖屏展示,而再从这个页面返回到导航页时,如果屏幕为竖屏且用户关闭了屏幕旋转锁定,则导航页应继续展示横屏。

下面笔记写的比较乱,可直接查看Demo in Github

屏幕旋转的方案

我现在使用的解决方案是CMMotionManager,启动设备的运动更新,通过给定的队列向给定的处理程序提供数据,对屏幕旋转的监测。

CMMotionManager可以理解为是CoreMotion Framework的中央管理器,也可以理解为运动服务。这些服务提供了获取加速机数据、旋转数据和磁力数据等。

首先我在info.plist中把设备的方向只勾选了竖屏。

AppDelegate 中需要实现的方法

- (UIInterfaceOrientationMask)application:(UIApplication*)application supportedInterfaceOrientationsForWindow:(UIWindow*)window {
    // iPhone doesn't support upside down by default, while the iPad does.  Override to allow all orientations always, and let the root view controller decide what's allowed (the supported orientations mask gets intersected).
    
    UIViewController * presentdeVC = [self.class topViewControllerWithPresentedViewController];
    if ([presentdeVC isKindOfClass:NSClassFromString(@"NaviController")])
        return UIInterfaceOrientationMaskAllButUpsideDown;
    
    return UIInterfaceOrientationMaskPortrait;
}

监听设备运动

/// 开启屏幕旋转的检测
- (void)startListeningDirectionOfDevice {
    if (self.motionManager == nil) {
        self.motionManager = [[CMMotionManager alloc] init];
    }
    
    // 提供设备运动数据到指定的时间间隔 刷新数据的评率
    self.motionManager.deviceMotionUpdateInterval = 0.3;
    
    // 判断设备传感器是否可用
    if (self.motionManager.deviceMotionAvailable) {
        // 启动设备的运动更新,通过给定的队列向给定的处理程序提供数据。
        [self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
            
            [self performSelectorOnMainThread:@selector(handleDeviceMotion:) withObject:motion waitUntilDone:YES];
        }];
    } else {
        [self setMotionManager:nil];
    }
}

停止监听设备运动

- (void)stopListeningDirectionOfDevice {
    if (_motionManager) {
        [_motionManager stopDeviceMotionUpdates];
        _motionManager = nil;
    }
}

根据设备运动,更新设备屏幕方向

- (void)handleDeviceMotion:(CMDeviceMotion *)deviceMotion {
    if ([UIDevice bb_canRotate] == false) {
        return;
    }
    
    double x = deviceMotion.gravity.x;
    double y = deviceMotion.gravity.y;
    
    if (fabs(y) >= fabs(x)) {// 竖屏
        if (y < 0) {
            if (self.previousInterfaceOrientation == UIInterfaceOrientationPortrait) {
                return;
            }
            [self applyInterfaceOrientation:UIInterfaceOrientationPortrait];
        }
        else {
            if (self.previousInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
                return;
            }
            [self applyInterfaceOrientation:UIInterfaceOrientationPortraitUpsideDown];
        }
    }
    else { // 横屏
        if (x < 0) {
            if (self.previousInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {
                return;
            }
            [self applyInterfaceOrientation:UIInterfaceOrientationLandscapeRight];
        }
        else {
            if (self.previousInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
                return;
            }
            [self applyInterfaceOrientation:UIInterfaceOrientationLandscapeLeft];
        }
    }
}

强制修改屏幕方向

- (void)applyInterfaceOrientation:(UIInterfaceOrientation)orientation {
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    
    UIDevice *currentDevice = [UIDevice currentDevice];
    UIDeviceOrientation currentDeviceOrientation = currentDevice.orientation;
    /// mark:强制旋转有时无效的解决方案: 强制前先设置为UIDeviceOrientationUnknown
    [currentDevice setValue:@(UIDeviceOrientationUnknown) forKey:@"orientation"];
    
    SEL selector = NSSelectorFromString(@"setOrientation:");
    if (![currentDevice respondsToSelector:selector]) {
        return;
    }
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
    [invocation setSelector:selector];
    [invocation setTarget:currentDevice];
    // 从2开始是因为0 1 两个参数已经被selector和target占用
    [invocation setArgument:&orientation atIndex:2];
    [invocation invoke];
    [currentDevice endGeneratingDeviceOrientationNotifications];
    [UIDevice setForceOrientation:currentDeviceOrientation];
}

获取屏幕锁定状态

现在存在的问题是,屏幕旋转开关被锁定时,横屏状态下不应该可以旋转页面,而Apple提供的api中,并没有获取控制中心中屏幕旋转锁定的开关

通过监听UIDeviceOrientationDidChangeNotification通知测试:

  • 当app在前台时,如果屏幕旋转开关锁定了,怎么旋转设备都不会触发此通知,只有在app启动、从后台进入前台或者被激活时,才会触发此通知
  • 当屏幕旋转开关被关闭时,设备旋转时都会触发此通知的

解决方法:

  • 注册UIDeviceOrientationDidChangeNotification通知
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientaionDidChange:) name:UIDeviceOrientationDidChangeNotification object:[UIDevice currentDevice]];
}
  • 当触发监听的方法时,根据设备的方向确定屏幕是否可以旋转
/// 当用户锁定了屏幕旋转开关时,且app在前台时,设备旋转不会触发此通知,当app启动、从后台进入前台或者被激活时,都会触发此通知
+ (void)deviceOrientaionDidChange:(NSNotification *)noty {
    self.forceOrientation = [UIDevice currentDevice].orientation;
    [InterfaceOrientationUtil sharedInsatnce].previousInterfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;
    
    UIDevice *device = [UIDevice currentDevice] ;
    /**
     *  取得当前Device的方向,Device的方向类型为Integer
     *
     *  必须调用beginGeneratingDeviceOrientationNotifications方法后,此orientation属性才有效,否则一直是0。orientation用于判断设备的朝向,与应用UI方向无关
     *
     *  @param device.orientation
     *
     */
    NSLog(@"%@", noty.userInfo);
    
    switch (device.orientation) {
        case UIDeviceOrientationLandscapeLeft:
        case UIDeviceOrientationLandscapeRight:
            [self setBb_canRotate:YES];//只有当用户把手机旋转到横屏的时候来去触发判断是否支持横屏
            break;
        default:
            [self setBb_canRotate:NO];
            break;
    }
}

在监听设备的运动更新的方法中,根据canRotate判断是否可以旋转屏幕

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,923评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,154评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,775评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,960评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,976评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,972评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,893评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,709评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,159评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,400评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,552评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,265评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,876评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,528评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,701评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,552评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,451评论 2 352

推荐阅读更多精彩内容

  • 在开发中,有时候我们需要让某些特定的页面为横屏展示,而从这个页面离开或者进入其他页面时为竖屏,起初我仅仅依赖了UI...
    MTDeveloper阅读 5,715评论 0 2
  • 关于屏幕旋转需要理解两个概念设备方向(UIDeviceOrientation)和屏幕方向(UIInterfaceO...
    骑马纵天下阅读 8,079评论 0 13
  • 专业考题类型管理运行工作负责人一般作业考题内容选项A选项B选项C选项D选项E选项F正确答案 变电单选GYSZ本规程...
    小白兔去钓鱼阅读 8,984评论 0 13
  • 1.开学练习普通话 3月中下旬考试 1.暑期社会实践计划 1.英语四级一定要过! 考虫5月中旬到期 2.减肥 ...
    就静静听你说阅读 173评论 0 0
  • 世上最美丽的是妈妈 最善良的是妈妈 最可爱的是妈妈 最慈祥的是妈妈 最伟大的是妈妈 妈妈,多么亲切的称呼 可我不会...
    吕玥妈咪阅读 200评论 1 0