iOS Window详解

概述

iOS 中的 KeyWindow,很多做iOS开发的小伙伴一定都知道这个属性,但是能彻底讲清楚它的,却是凤毛麟角,今天笔者带大家一起揭开这层面纱。

附上下文所用的Demo Github链接

KeyWindow是什么?

苹果官方文档《Multiple Display Programming Guide for iOS》中是这样解释的

A window is considered the key window when it is currently receiving keyboard and non touch-related events. Whereas touch events are delivered to the window in which the touch occurred, events that don’t have an associated coordinate value are delivered to the key window. Only one window at a time can be key.

Most of the time, the app window becomes the key window. Because iOS uses separate windows to display alert views and input accessory views, these windows can also become key. For example, when an alert or input accessory view has a text field in which the user is currently typing, the window that contains the input view is key.

翻译过来如下:

当一个窗口目前正在接收键盘和非触摸相关的事件时,它被认为是KeyWindow。触摸事件被传递到发生触摸的窗口,而没有相关坐标值的事件被传递到KeyWindow。一次只能有一个窗口是KeyWindow
大多数情况下,应用程序窗口会成为关键窗口。因为iOS使用单独的窗口来显示警报视图和输入附件视图,这些窗口也可以成为关键。例如,当警报或输入附件视图有一个用户目前正在输入的文本字段时,包含输入视图的窗口就是关键。

翻译的很不好,小伙伴领悟大概意思即可。

KeyWindow 和 普通Window的区别

普通Window也是可以正常响应触摸事件的,但是不可以响应非触摸事件。这里的非触摸事件,包含:摇晃等运动传感器产生的事件、远程控制(AirPlay投射,耳机线控,车载系统显示)等事件。

我们可以简单的写几行代码,测试一下摇一摇功能。

@interface YZKWindow : UIWindow
@end

@implementation YZKWindow
  
// 新建一个Window类,重写UIResponder的方法,接收摇晃事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event {
    if (motion == UIEventSubtypeMotionShake)
    {
        NSLog(@"摇一摇");
    }
}
@end
  
// 当前展示页面的Controller文件中
// 我们触发一个点击事件,具体操作如下:
- (void)btnclick {
    NSLog(@"1 %@", [UIApplication sharedApplication].windows);
    UIWindow *window = [[YZKWindow alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    NSLog(@"2 %@", [UIApplication sharedApplication].windows);
    window.backgroundColor = [UIColor brownColor];
    window.windowLevel = 100;
    window.hidden = NO;
    self.window2 = window;
    NSLog(@"3 %@", [UIApplication sharedApplication].keyWindow);

    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    button.frame = CGRectMake(0, 0, 100, 30);
    [button setTitle:@"button" forState:UIControlStateNormal];
    [button addTarget:self action:@selector(btn2Click) forControlEvents:UIControlEventTouchUpInside];
    [window addSubview:button];
}

如上述代码所示,我们创建了一个YZKWindow的实例,并且没有 makeKeyAndVisible。通过直接设置 window.hidden = NO ,即可在页面上展示Window。此时我们摇一摇手机,我们可以看到,并没有打印“摇一摇”的log。

这是因为此时的KeyWindow是我们应用的主Window,即appdelegate.window(iOS13往后sceneDelegate.window类似)。此时的摇晃事件,会从Application传递给当前的KeyWindow。由于UIWindow也继承自UIResponder,这个事件会根据事件的传递链以及响应链去查找对应的响应者。显然我们的YZKWindow不在链条中,所以不会触发事件。

这里我们在YZKWindow上,放置了一个Button,我们测试发现,Button是可以被正常点击响应的。即非KeyWindow是可以响应触摸事件的。

KeyWindow 是哪个?

上文的Demo,如果我们在YZKWindow的Button点击事件中,打印当前的KeyWindow,可以发现,当前的YZKWindow变成了KeyWindow。

这说明了系统会在Button点击时自动帮我们切换KeyWindow。

同理,当我们使用UIAlertController(带TextField)或弹出键盘(带AccessoryView)的时候,系统都有可能会切换KeyWindow。

由于切换KeyWindow会影响非触摸事件的响应,当我们在做一些自定义Window展示的时候,推荐重写UIWindow 的 - (BOOL)canBecomeKeyWindow 方法,使其不可变为KeyWindow。

Normal Window的展示

很多开发的小伙伴,在创建完Window后,会认为只有调用 makeKeyAndVisible 才能展示出Window,这其实是错误的。上文已经演示了,这里我们仅需简单的设置 window.hidden = NO,就可以让Window展示。

事实上,当我们创建完成一个Window后,通过 [UIApplication sharedApplication].windows 方法,我们可以看到,其实已经添加到我们的Application上了。

需要注意, [UIApplication sharedApplication].windows 数组并不会强引用Window,如果不将Window的实例强引用保存起来,它会很快释放,导致页面不能展示对应的window实例。

makeKeyAndVisible 实际上做的事情,也就是调用了 makeKeyWindowwindow.hidden = NO 两件事。

综上所述,如果小伙伴需要做一个显示在页面最上方的Window(eg:悬浮球,直播小窗),推荐直接设置 hidden 即可,没必要修改KeyWindow,可能会导致应用主Window相关非触摸事件不响应。

Window展示的逻辑,与是否是KeyWindow无关,与 hiddenwindowLevel 有关。

iOS13往后sceneDelegate.window类似,这里就不细讲。

PresentViewController

项目在开发过程中,经常会遇到需要PresentViewController的情况。如果是在一个UI组件中,可以快速便捷的直接在UI组件所在的Controller上Present。如果是在一段业务逻辑代码中,这个时候,我们一般有如下两种方式获取Controller(iOS13sceneDelegate类似)。

  1. [UIApplication sharedApplication].delegate.window

  2. [UIApplication sharedApplication].keyWindow

上文我们已经讲过,KeyWindow是会被系统修改的,所以不一定是应用的主Window,所以使用第二种方式去present,可能会导致UI异常,推荐使用第一种方式去获取Window。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容