iOS -- UIWindow的使用
现在有一个需求:
在判断出用户token过期,或者没有登录的时候显示登录界面
这个需求实现起来有三个方法:
1 改变UIApplication的window的根控制器
2 在当前控制器present出来登录界面
3 通过添加window显示登录界面
前两种方法的弊端:
第一种方法会销毁之前的控制器,登陆成功之后需要重新加载,可能无法回到登陆之前的界面
第二种方法只能在控制器中实现,如果是在一个view中,就无法用这种方式实现
综上所述,第三种方法是最通用,效率最高的
关于UIWindow的介绍:
一、UIWindow特点
(1)UIWindow 是一种特殊的 UIView,通常在一个 app 中至少会有三个 UIWindow:
- app delegate里的window
- 状态栏的window(比较特殊,虽然在程序内部可以调用某些api显示隐藏或改变其UI,但它的window是不被我们的应用程序内部所持有的)
- 键盘的window
(2)iOS程序启动完毕后,创建的第一个视图控件就是 UIWindow,接着创建控制器的 view,最后将控制器的 view 添加到 UIWindow 上,于是控制器的 view 就显示在屏幕上了;
(3)一个iOS程序之所以能显示到屏幕上,完全是因为它有 UIWindow,也就说,没有UIWindow,就看不见任何UI界面。
二、添加 UIView 到 UIWindow 中两种常见方式:
(1)- (void)addSubview:(UIView )view;*
直接将 view 添加到 UIWindow 中,但并不会理会 view 对应的 UIViewController;
(2)设置window的rootViewController;
这种做法会自动将 rootViewController 的 view 添加到 UIWindow 中,是苹果推荐的做法
三、UIWindow 常用方法
- (void)makeKeyWindow;
让当前 UIWindow 变成 keyWindow(主窗口);
- (void)makeKeyAndVisible;
让当前 UIWindow 变成 keyWindow,并显示出来。
四、UIWindow 的获得
[UIApplication sharedApplication].windows
在本应用中打开的 UIWindow 列表,这样就可以接触应用中的任何一个 UIView 对象;
(平时输入文字弹出的键盘,就处在一个新的 UIWindow 中)
[UIApplication sharedApplication].keyWindow
用来接收键盘以及非触摸类的消息事件的 UIWindow,而且程序中每个时刻只能有一个 UIWindow 是 keyWindow。如果某个 UIWindow 内部的文本框不能输入文字,可能是因为这个 UIWindow 不是 keyWindow。
如果想通过这个方法改变keyWindow的rootviewcontroller来改变显示的界面,可能无法成功
window,多个时,[UIApplication sharedApplication].windows[0]和[[UIApplication sharedApplication] delegate] window]获取的是最开始创建的window,而[UIApplication sharedApplication].keyWindow获取的是当前显示的window,不一定是最初的window
正确的代码是
UIWindow * win = [UIApplication sharedApplication].delegate.window;
[win setRootViewController:vc];
[win makeKeyAndVisible];
五、UIWindow 层级
self.window.windowLevel = UIWindowLevelAlert;
UIWindow 有三个层级:UIWindowLevelAlert, UIWindowLevelStatusBar, UIWindowLevelNormal,
Normal,StatusBar,Alert 分别 为 0,1000,2000
windowLevel 是 CGFloat 类型,可以进行加减运算,或自定义优先级
现在我们来解决问题:创建一个自己的window类,根控制器设置为登陆控制器,通过控制window的hidden属性来显示或隐藏,然后在需要的时候将window显示出来,登陆完成后隐藏
注意:想要自己的window显示,就要保证windwo对象不能被销毁,需要保持在内存中,比较方便的做法是将window设置成单例,就可以保存在内存中,在不需要的时候设置为nil,释放内存
#import <UIKit/UIKit.h>
@interface LogInWindow : UIWindow
+ (instancetype)sharedLogInWindow;
+ (void)hideWindow;
@end
#import "LogInWindow.h"
#import "LogInVC.h"
static LogInWindow * __logWin;
@implementation LogInWindow
+ (instancetype)sharedLogInWindow{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__logWin = [[LogInWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UIStoryboard * logSB = [UIStoryboard storyboardWithName:@"LogInSB" bundle:[NSBundle mainBundle]];
UINavigationController * logNav = [logSB instantiateViewControllerWithIdentifier:@"LogInNav"];
__logWin.rootViewController = logNav;
});
return __logWin;
}
+ (void)hideWindow{
[LogInWindow sharedLogInWindow].hidden = YES;
}
@end
UIWindow使用需要注意的地方:
切换根控制器可能会造成内存泄漏:如果present了一个控制器,在当前控制器切换rootViewController,而没有执行dismiss操作,就会导致控制器无法释放,造成内存泄漏
在切换控制器之前需要执行dismiss操作
[self dismissViewControllerAnimated:YES completion:^{
//在这里更换根控制器
}];
获取最上层window的方法:
- (UIWindow *)lastWindow
{
NSArray *windows = [UIApplication sharedApplication].windows;
for(UIWindow *window in [windows reverseObjectEnumerator]) {
if ([window isKindOfClass:[UIWindow class]] &&
CGRectEqualToRect(window.bounds, [UIScreen mainScreen].bounds))
return window;
}
return [UIApplication sharedApplication].keyWindow;
}
https://www.jianshu.com/p/98cd7fc4bfba这个是大神写的,超级厉害
https://yq.aliyun.com/articles/62456 这个阿里云的文章写的云里雾里,但是好像很牛逼的样子,先存起来

浙公网安备 33010602011771号