在做视频开发时遇到屏幕旋转问题,其中涉及到 statusbar、 uinavigationcontroller、uitabbarcontroller 、uiviewcontroller
。
在设备锁屏下的整体效果图
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
下面还会举例说明。
其实 uideviceorientation
与 uiinterfaceorientation
是两个互不相干的属性,通常情况下会一起出现,在这里正好利用此特性在屏幕旋转后进行重新布局。
第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