iOS屏幕旋转与锁屏的示例代码

时间:2022-09-19 21:30:45

在做视频开发时遇到屏幕旋转问题,其中涉及到 statusbar、 uinavigationcontroller、uitabbarcontroller 、uiviewcontroller

在设备锁屏下的整体效果图

 

iOS屏幕旋转与锁屏的示例代码

ios-旋转.gif

主要涉及以下4点:

  • 横竖屏的旋转
  • 屏幕旋转相应改变视图位置
  • 旋转时状态栏的隐藏与显示
  • 锁屏

1、横竖屏旋转

第1步:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
-(uiinterfaceorientationmask)application:(uiapplication *)application supportedinterfaceorientationsforwindow:(uiwindow *)window {
 
//  nslog(@"0000000---------%@",nsstringfromclass([[self topviewcontroller] class]));
//  if ([nsstringfromclass([[self topviewcontroller] class]) isequaltostring:@"firstviewcontroller"]) {
//    //横屏
//    return uiinterfaceorientationmasklandscaperight;
//  }
//  //竖屏
//  return uiinterfaceorientationmaskportrait;
  
  nsuinteger orientations = uiinterfaceorientationmaskallbutupsidedown;
 
  if(self.window.rootviewcontroller){
    //取出当前显示的控制器
    uiviewcontroller *presentedviewcontroller = [self topviewcontrollerwithrootviewcontroller:self.window.rootviewcontroller];
    //按当前控制器支持的方向确定旋转方向(将旋转方向重新交给每个控制器自己控制)
    nslog(@"%s, line = %d",__function__,__line__);
    orientations = [presentedviewcontroller supportedinterfaceorientations];
  }
 
  return orientations;
}
//获取界面最上层的控制器
//- (uiviewcontroller*)topviewcontroller {
//  nslog(@"%s, line = %d",__function__,__line__);
//  return [self topviewcontrollerwithrootviewcontroller:[uiapplication sharedapplication].keywindow.rootviewcontroller];
//}
//一层一层的进行查找判断
- (uiviewcontroller*)topviewcontrollerwithrootviewcontroller:(uiviewcontroller*)rootviewcontroller {
  nslog(@"%s, line = %d",__function__,__line__);
  if ([rootviewcontroller iskindofclass:[uitabbarcontroller class]]) {
    
    uitabbarcontroller* tabbarcontroller = (uitabbarcontroller*)rootviewcontroller;
    nslog(@"tabbar:%@",nsstringfromclass([tabbarcontroller.selectedviewcontroller class]));
 
    return [self topviewcontrollerwithrootviewcontroller:tabbarcontroller.selectedviewcontroller];
  } else if ([rootviewcontroller iskindofclass:[uinavigationcontroller class]]) {
    
    uinavigationcontroller* nav = (uinavigationcontroller*)rootviewcontroller;
    nslog(@"nav:%@",nsstringfromclass([nav.visibleviewcontroller class]));
    return [self topviewcontrollerwithrootviewcontroller:nav.visibleviewcontroller];
  } else if (rootviewcontroller.presentedviewcontroller) {
    nslog(@"present:%@",nsstringfromclass([rootviewcontroller.presentationcontroller class]));
    uiviewcontroller* presentedviewcontroller = rootviewcontroller.presentedviewcontroller;
    return [self topviewcontrollerwithrootviewcontroller:presentedviewcontroller];
  } else {
    nslog(@"root:%@",rootviewcontroller);
    return rootviewcontroller;
  }
}

代码中通过 -(uiinterfaceorientationmask)application:(uiapplication *)application supportedinterfaceorientationsforwindow:(uiwindow *)window 方法将控制器交给自己控制,该方法默认值为 info.plist 中配置的 supported interface orientations 项的值。

第2步:在各控制器设置支持的方向

?
1
2
3
4
5
6
7
8
9
//是否允许旋转(默认允许)
- (bool)shouldautorotate {
  return yes;
}
 
- (uiinterfaceorientationmask)supportedinterfaceorientations{
  //允许旋转的方向
  return uiinterfaceorientationmaskall;
}

其中 - supportedinterfaceorientations 方法在 ipad 中默认取值为 uiinterfaceorientationmaskall ,即默认支持所有屏幕方向;而 iphone 跟 ipod touch 的默认取值为 uiinterfaceorientationmaskallbutupsidedown ,即支持除竖屏向下以外的三个方向。

在设备屏幕旋转时,系统会调用 - shouldautorotate 方法检查当前界面是否支持旋转,只有 - shouldautorotate 返回 yes 的时候, - supportedinterfaceorientations 方法才会被调用,以确定是否需要旋转界面。

这个是 tabbarcontroller 中设置的,它会影响关联的 uiviewcontroller 的支持方向,需要在 uiviewcontroller 中进一步设置

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//此方法来控制能否横竖屏 控制锁屏
 - (uiinterfaceorientationmask)supportedinterfaceorientations {
   nslog(@"%s, line = %d",__function__,__line__);
   uiinterfaceorientationmask inter;
   if (_lockscreen) {
     switch (_lockorientation) {
       case 1:
         inter = uiinterfaceorientationmaskportrait;
         break;
       case 2:
         inter = uiinterfaceorientationmaskportraitupsidedown;
         break;
       case 3:
         inter = uiinterfaceorientationmasklandscaperight;
         break;
       case 4:
         inter = uiinterfaceorientationmasklandscapeleft;
         break;
       default:inter = uiinterfaceorientationmaskall;
         break;
     }
   } else {
     inter = uiinterfaceorientationmaskall;
   }
   //支持全部方向
   return inter;
 }

第3步:强制转换控制器方向

?
1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)setinterorientation:(uiinterfaceorientation)orientation {
   
   if ([[uidevice currentdevice] respondstoselector:@selector(setorientation:)]) {
     sel selector       = nsselectorfromstring(@"setorientation:");
     nsinvocation *invocation = [nsinvocation invocationwithmethodsignature:[uidevice instancemethodsignatureforselector:selector]];
     [invocation setselector:selector];
     [invocation settarget:[uidevice currentdevice]];
     int val         = orientation;
     // 从2开始是因为0 1 两个参数已经被selector和target占用
     [invocation setargument:&val atindex:2];
     [invocation invoke];
   }
 }

这样就可以完成横竖屏的切换。

2、屏幕旋转相应改变视图位置

这里先扩展 uideviceorientation & uiinterfaceorientation 的知识

uideviceorientation 设备的物理方向

uideviceorientation 即我们手持的移动设备的 orientation ,是一个三围空间,有六个方向,通过 [uidevice currentdevice].orientation 获取当前设备的方向。

?
1
2
3
4
5
6
7
8
9
typedef ns_enum(nsinteger, uideviceorientation) {
  uideviceorientationunknown,
  uideviceorientationportrait,     
  uideviceorientationportraitupsidedown, // device oriented vertically, home button on the top 竖屏向下,即头在下,home 键在上
  uideviceorientationlandscapeleft,    // device oriented horizontally, home button on the right 横屏头在左,home键在右
  uideviceorientationlandscaperight,   // device oriented horizontally, home button on the left 横屏头在右,home键在左
  uideviceorientationfaceup,       // device oriented flat, face up
  uideviceorientationfacedown       // device oriented flat, face down
} ;

uiinterfaceorientation 界面的显示方向

uiinterfaceorientation 即我们看到的视图的 orientation ,可以理解为 statusbar 所在的方向,是一个二维空间,有四个方向, 通过 [uiapplication sharedapplication].statusbarorientation 即状态栏的方向获取当前界面方向。

?
1
2
3
4
5
6
7
8
9
// 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
}

uiinterfaceorientationmask 支持的方向

?
1
2
3
4
5
6
7
8
9
10
// ios 6 之后用于控制界面的枚举值
typedef ns_options(nsuinteger, uiinterfaceorientationmask) {
 uiinterfaceorientationmaskportrait = (1 << uiinterfaceorientationportrait),
 uiinterfaceorientationmasklandscapeleft = (1 << uiinterfaceorientationlandscapeleft),
 uiinterfaceorientationmasklandscaperight = (1 << uiinterfaceorientationlandscaperight),
 uiinterfaceorientationmaskportraitupsidedown = (1 << uiinterfaceorientationportraitupsidedown),
 uiinterfaceorientationmasklandscape = (uiinterfaceorientationmasklandscapeleft | uiinterfaceorientationmasklandscaperight),
 uiinterfaceorientationmaskall = (uiinterfaceorientationmaskportrait | uiinterfaceorientationmasklandscapeleft | uiinterfaceorientationmasklandscaperight | uiinterfaceorientationmaskportraitupsidedown),
 uiinterfaceorientationmaskallbutupsidedown = (uiinterfaceorientationmaskportrait | uiinterfaceorientationmasklandscapeleft | uiinterfaceorientationmasklandscaperight),
}

由上可以发现:

ios 6 及之后版本使用的 uiinterfaceorientationmask 类型来控制屏幕屏幕方向,该类型也新增加了几个枚举取值,可用一个枚举取值来代表多个屏幕方向,使用起来更方便。

注意在 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,大意是界面的左转相当于设备的右转,如果设备向左转时就需要内容(即界面)向右转。即:

uiinterfaceorientationlandscapeleft = uideviceorientationlandscaperight
uiinterfaceorientationlandscaperight = uideviceorientationlandscapeleft

下面还会举例说明。

其实 uideviceorientationuiinterfaceorientation 是两个互不相干的属性,通常情况下会一起出现,在这里正好利用此特性在屏幕旋转后进行重新布局。

第1步:监听 uideviceorientationdidchangenotification 状态

?
1
2
3
4
5
6
7
8
//监听设备旋转 改变 视图 对应位置
 [[nsnotificationcenter defaultcenter] addobserver:self selector:@selector(deviceorientationdidchange) name:uideviceorientationdidchangenotification object:nil];
 
//用来控制横竖屏时调整视图位置
- (void)deviceorientationdidchange
{
  [self isportrait];
}

第2步:重新布局

?
1
2
3
4
5
6
7
8
if (_interorientation == uiinterfaceorientationportrait || _interorientation == uiinterfaceorientationportraitupsidedown) {
     self.top.constant = 145;
     self.bottom.constant = 210;
     
   } else if (_interorientation == uiinterfaceorientationlandscaperight || _interorientation == uiinterfaceorientationlandscapeleft) {
     self.top.constant = 40;
     self.bottom.constant = 50;
   }

例如:竖屏转横屏

界面竖屏 uiinterfaceorientationportrait ->横屏 uiinterfaceorientationlandscaperight ,设备方向 uideviceorientationportrait -> uideviceorientationlandscapeleft ,在设备发生变化这个过程触发 uideviceorientationdidchangenotification 监听,然后进行重新布局。

3、旋转时状态栏的隐藏与显示

这里只记述旋转时状态栏的变化,由竖屏想横屏变化时状态栏会消失。

?
1
2
3
4
5
//在需要的`uiviewcontroller`设置是否隐藏
- (bool)prefersstatusbarhidden {
 nslog(@"%s, line = %d",__function__,__line__);
 return no;
}

4、锁屏

锁屏时,不管系统锁屏是否关闭、push 或 present 返回后,界面依然保持不变。

第1步:设置锁屏

?
1
2
3
4
5
6
7
8
9
10
11
12
- (ibaction)lockaction:(uibutton *)sender {
   if (_lockscreen) {
     
     _lockscreen = no;
     [sender settitle:@"锁定屏幕" forstate:uicontrolstatenormal];
   } else {
     _lockscreen = yes;
     
     [sender settitle:@"解开屏幕" forstate:uicontrolstatenormal];
   }
   _lockorientation = _interorientation;
 }

第2步:绕过强转

?
1
2
3
4
5
6
7
8
- (void)interfaceorientation:(uiinterfaceorientation)orientation
 {
   
   [self isportrait];
   //锁屏情况下 不旋转
   if (!_lockscreen) {
     [self setinterorientation:orientation];
   }

第3步:针对 push 或 present 返回后

?
1
2
3
4
5
6
7
8
9
- (void)viewwillappear:(bool)animated {
   
   if (_lockscreen) {
     //记录返回时的界面状态
     [self setinterorientation:_lockorientation];
   } else {
    [self isportrait];
   }
 }

5、 针对特定 uiviewcontroller 方向的支持

?
1
2
3
4
5
6
7
8
9
-(uiinterfaceorientationmask)application:(uiapplication *)application supportedinterfaceorientationsforwindow:(uiwindow *)window {
 
   if ([nsstringfromclass([[self topviewcontroller] class]) isequaltostring:@"firstviewcontroller"]) {
     //横屏
     return uiinterfaceorientationmasklandscaperight;
   }
   //竖屏
   return uiinterfaceorientationmaskportrait;
 }

最后的献上 github 代码,还有2个小的 bug ,有兴趣的朋友欢迎来探讨。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://www.jianshu.com/p/5f82baaab740