iOS · 2023年 9月 2日 0

基于堆的弹窗(AlertView)管理器

一、背景

目前项目上使用自定制的AlertView,但弹窗缺乏管理,在处理某些业务场景时,往往需要额外的代码去处理,不够优雅。举一些的例子:
场景1:

当前App页面已经存在一个弹窗,用户没有理会,后台无操作一段时间后回到前台,会触发超时弹窗,点击弹窗按钮触发登录逻辑,此时期望清除掉原有页面的弹窗。

场景2:

登录成功后,根据用户的个人情况(如身份证过期)弹出一些弹窗,同时可能存在一些营销弹窗需要弹出。

二、需求分析

根据目前已知的场景,我们需要设计一个弹窗管理器来统一管理弹窗,其要具备以下能力:

  • 优先级
  • 可清理
  • 弹窗与管理器解耦

因为需要对新弹窗进行排序(比较优先级),因此数据结构选择了堆,下面聊下基本思路:

展示:当创建一个item(待展示的弹框)后,将其入堆进行维护,比较其与当前展示的currentItem的优先级,若优先级高于currentItem,则currentItem所代表的item消失,展示item,同时currentItem = item

删除:当点击currentItem 指向的弹窗的按钮后,删除堆中对应的item,并维护堆的正确性

三、设计

1.优先级

在创建待展示的弹窗时,我们可以为其配置一个权重值来确定展示的优先级,当权重值相同时,优先展示后入堆的,即:

优先级 = 综合权重值 = 权重值 + 序列值

2.可清理

依赖于堆的特性,我们可以很方便的清理堆中的元素,考虑到业务场景,我们可以为弹窗配置一个clearOther的选项,当点击该弹窗的按钮时,判断其clearOther,若为true,则清空堆。

3.弹窗与管理器解耦

在写demo时,使用了一个名为DAlertView的简洁的开源弹窗,考虑到在应用时,往往项目都有自己的弹窗样式,因此使用了一个桥接类来实现弹窗与管理器的解耦,避免在做兼容修改时,不小心改动了管理逻辑。

四、实现

本身利用堆的特性来做这个事情思路是比较清晰的,但在实践中,消耗时间最多的是处理线程问题,是的,没错,开发准则要求我们要在子线程处理数据,在主线程更新UI,那么就需要协调两个线程之间的关系,主要原则:

  • 在子线程维护堆
  • 在准备弹窗前挂起子线程,待弹窗展示完毕后,载恢复
  • 弹窗准备消失前,阻塞主线程,维护堆,然后恢复主线程。这里牺牲了性能,但是目前没有想到更好的办法去处理快速点击时维护堆产生的错乱

五、Demo

由于思路比较清晰,无甚难度,故没有画程序流程图(偷懒)。demo见地址