RunLoop 和 NSTimer
每次显示时先要将之前设置过的 timer 取消
// cancel previous dismissing & remove animations
[[NSRunLoop currentRunLoop] cancelPerformSelector:@selector(dismiss) target:self argument:nil];
timer 的设置代码如下:
- (void)setDismissTimerWithInterval:(NSTimeInterval)interval; {
[self.dismissTimer invalidate]; // 重新赋值时先要使其无效
self.dismissTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:interval]
interval:0 target:self selector:@selector(dismiss:) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:self.dismissTimer forMode:NSRunLoopCommonModes];
}
为了能在用户拖拽页面的时候 timer 正确计时,将 timer 添加到 NSRunLoopCommonModes 模式下
UIWindowLevel
每个应用都有一个主 window,通常在 AppDelegate 中设置
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:
[[SBExampleViewController alloc] initWithStyle:UITableViewStyleGrouped]];
[self.window makeKeyAndVisible];
makeKeyAndVisible 方法是将某个 window 设置为主窗口并设置为可见,这个窗口的级别使用默认值 UIWindowLevelNormal,UIWindowLevel 的级别如下
UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelAlert;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar __TVOS_PROHIBITED;
其级别顺序是 Normal < StatusBar < Alert,也就是说 Alert 级别的 window 会在最前端显示。
其中 UIKIT_EXTERN 的定义如下
#ifdef __cplusplus
#define UIKIT_EXTERN extern "C" __attribute__((visibility ("default")))
#else
#define UIKIT_EXTERN extern __attribute__((visibility ("default")))
#endif
这几个变量的定义在该文件中可见
extern “C” 是连接申明(linkage declaration),被 extern “C” 修饰的变量和函数是按照 C 语言方式编译和连接的
所以我们如果用 AppDelegate 中创建的这个最低级别作为显示状态栏的窗口的话,很可能会被遮盖,所以首先要创建一个状态栏级别的窗口,创建窗口的代码见 - (UIWindow *)overlayWindow; 方法。
self.overlayWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.overlayWindow.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.overlayWindow.backgroundColor = [UIColor redColor];
self.overlayWindow.userInteractionEnabled = NO;
self.overlayWindow.windowLevel = UIWindowLevelStatusBar;
self.overlayWindow.rootViewController = [[UIViewController alloc] init];
self.overlayWindow.rootViewController.view.backgroundColor = [UIColor clearColor];
self.overlayWindow.hidden = NO;
需要注意的是,窗口创建后默认是不显示的,设置 hidden 为 NO 即可显示,如上我们创建了一个红色的窗口,它可以和 AppDelegate 中设置的主窗口并存,由于它的级别高,所以完全覆盖了主窗口显示红色。
屏幕旋转
当屏幕旋转时,我们创建的 overlayWindow 也要处理旋转,它的旋转 transform 应当和主窗口相同,mainApplicationWindowIgnoringWindow 方法用来获取主窗口,之后将 overlayWindow 的 transform 和 frame 设置为主窗口一致即可,overlayWindow 中的 topBar 的 frame 也需要重新设置。