iOS UIWindow 覆盖 StatusBar

最近产品要做一个消息的通知,而且通知是覆盖在 StatusBar 上面的。如果是普通的 UIView 则肯定是不行的,因为 StatusBar 为系统全局性的视图,所以要想覆盖它,则必须为 Statusbar 类型的,那么也就是 UIWindow 了。

UIWindow 的基础介绍

  • UIWindow是一种特殊的UIView,通常在一个app中至少会有一个UIWindow。
  • iOS程序启动完毕后,创建的第一个视图控件就是UIWindow,接着创建控制器的View,最后将控制器的View添加到UIWindow上,于是控制器的View就显示在屏幕上了。
  • 一个iOS程序之所以能显示在屏幕上,完全是因为它有UIWindow,也就是说,没有UIWindow就看不到任何UI界面。
  • 状态栏和键盘都是特殊的UIWindow。

这里有三个重要的对象UIScreen,UIWindow,UIView。

  1. UIScreen对象识别物理屏幕连接到设备
  2. UIWindow对象提供绘画支持给屏幕
  3. UIView执行绘画,当窗口要显示内容的时候,UIView绘画出他们的内容并附加到窗口上。

UIWindow 等级

window等级,即window在z轴上的层级关系,默认是0。UIWindowLevel 本身是一个 CGFloat 类型,可以随意设置或进行加减,高等级会显示在低等级上面。系统给出了三种常用等级:

1
2
3
UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal;      0
UIKIT_EXTERN const UIWindowLevel UIWindowLevelAlert; 2000
UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar; 4000

初始化 UIWindow 覆盖 StatusBar

1
2
3
4
5
6
7
8
9
-(void)initWindow {
//初始化statusView,可在其上添加控件
CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
stateWindow = [[UIWindow alloc] initWithFrame:statusBarFrame];
// 设置windowLevel为statusbar + 1 保证可以显示在statusbar上面
stateWindow.windowLevel = UIWindowLevelStatusBar + 1;
stateWindow.backgroundColor = [UIColor orangeColor];
stateWindow.userInteractionEnabled = NO;
}

这样创建的与 statusbar 大小一样的UIWindow 已经好了,默认创建好 这个UIWindow 则已经添加上去了,只是默认不显示,可以使用下面来控制显示和隐藏。

1
2
window.hidden = YES; 
window.hidden = NO;

当然 UIwindow 还有个 makeKeyAndVisible 方法,这个方法会将新建的 UIWindow 设置为主窗口并显示,之后如果消失后,需要将原来的主窗口设置回来,不然会出现很多异常的问题,这里切记。

注意:控制新添加的 UIWindow 使用 hidden 来控制。尽量不要使用 makeKeyAndVisible

这里的 stateWindow 必须是私有变量或者全局变量,生命周期随所在控制器的生命周期。

添加Label

上面的 UIWindow 创建好了,那么要显示相关的提示文字,还需要添加 label,

1
2
3
4
5
6
label = [[UILabel alloc] initWithFrame:stateWindow.frame];
label.backgroundColor = [UIColor blackColor];
label.textColor = [UIColor whiteColor];
label.font=[UIFont systemFontOfSize:12];
label.textAlignment = NSTextAlignmentCenter;
[stateWindow addSubview:label];

之后设置 label的文字即可进行对其内容控制。

旋转问题处理

上面弄完基本上都完成,但是在旋转屏幕后,发现显示还是在原来的位置上,无法跟随系统的状态栏一起旋转。需要对 Window 设置空的UIViewController后才会跟着状态栏旋转。

1
2
// 需要设置一个空的ViewController 不然旋转屏幕后,不会跟着旋转坐标系
stateWindow.rootViewController = [UIViewController new];

旋转后,又发现了新的问题,在新建的 UIWindow 根据的是一开始时的状态长宽,但是旋转为横屏时,尺寸会发生变化,所以在每次显示 statusWindow 时重新设置下frame的大小,如下:

1
2
3
CGRect newFrame = CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, STATUSBAR_HEIGHT);
stateWindow.frame = newFrame;
label.frame = newFrame;

这里不直接取 statusbar 的frame,因为在statusbar 隐藏时获取的长宽为0,所以上面设置时取值为宽度和statusbar的高度,高度在一开始进行获取保存进常量中。

综上:对于显示覆盖 statusbar 的 UIWindow 已经完成啦,

其他细节

当我们显示了之后一般需要在延迟几秒后进行隐藏,在 Android 中一般实用Handler进行,或者实用timer进行执行操作。那么 ios 中怎么实用呢? 请看如下方法:

1
2
3
4
5
6
//延迟 3s 后执行 hideStateMsg 方法。
[self performSelector:@selector(hideStateMsg) withObject:nil afterDelay:3.0];

-(void)hideStateMsg {
stateWindow.hidden = YES;
}

当然在执行之前可能还需要把之前的延时任务取消了,则有如下两种方法。

1
2
3
4
5
// 取消 self 对象中所有的延迟任务。
[NSObject cancelPreviousPerformRequestsWithTarget:self];

// 取消指定的函数的延迟执行操作。
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideStateMsg) object:nil];

总结:

通过上面的一些问题处理解决,最终能够实现一个覆盖 Statusbar 的View,并且能够在指定时间后消失,达到消息提醒的功能。

参考地址:
https://www.cnblogs.com/CoderAlex/p/4762210.html

WangXin wechat
欢迎订阅我的微信公众号,第一时间获取最新文章!
坚持原创技术分享,您的支持将鼓励我继续创作!