WKViewManager iOS 彈窗架構

WangKunKun發表於2018-12-21

前言

近來由於App中彈窗過多,再加上還有半透明的引導層,時常會出現多個彈窗重疊彈出,甚至會伴隨引導層一起彈出,極大的影響了使用者體驗。

上述問題,其實很簡單,只需要新增一個彈窗佇列即可。

但是與此同時我們又新增了新的需求如下:

  1. 廣告彈窗不能出現在閱讀頁
  2. 訂閱更新彈窗只能出現在首頁
  3. 版本更新功能提示頁不允許出現任何彈窗
  4. 重新安裝後出現的選擇性別和分類的彈窗不允許出現任何彈窗
  5. 廣告觀賞頁面不能出現任何彈窗
  6. 由於廣告和公告頁面的彈出順序不定,當彈出廣告時,廣告檢視可點選產生跳轉,但是在跳轉後公告會彈出(公告具備倒數計時只顯示多少秒,然後自動消失),則會出現公告無法被使用者看到的情況,則需要在跳轉離開頁面時,暫停佇列

由於專案中我們採用了模組化的方式精簡程式碼、降低工作耦合,所以我們的彈窗並不是基於ViewController彈出的,而是在需要彈出的時候獲取當前顯示的ViewController用於顯示的。

例如專案中任務模組,任務是可以在任何地方任何時候被完成,任務本身不關心它是位於何時何地被完成,只需要關心完成後,需要對應做出什麼操作,例如寫本地資料庫,更新使用者資料,彈出完成任務提示,和ViewController並無大的關聯。

簡單的說,我們的彈窗是屬於功能塊的,而功能塊不應該且不用去關心當前展示的ViewController是哪一個。

功能介紹

本文介紹的一個彈窗架構實際也可以歸類為一個彈窗功能模組,當彈窗出現在不該出現的地方時,它來負責排程和處理,其過程對於其它功能模組透明,並不產生任何耦合度。其實現功能如下:

  1. 彈窗檢視按佇列依次出現
  2. 彈窗檢視支援優先順序模式
  3. 佇列基於ViewController,杜絕一個彈窗問題導致全域性彈窗失效
  4. 彈窗檢視可指定出現於某ViewController出現後(白名單)
  5. ViewController可指定不接收彈窗檢視出現於自身出現時(黑名單)
  6. 實現ViewController didDisAppear後 佇列暫停,willAppear佇列繼續
優點

WKViewManager 彈窗佇列管理類,使用AOP切片基於self-manager模式封裝,高內聚性,幾乎不會和ViewController或功能模組產生耦合。

劣勢

由於是彈窗檢視的一個管理工具,所有需要被管理的彈窗均需屬於WKBaseView的子類,專案中已書寫對應所需要的彈窗基類如WKPopBaseView、WKStepMaskGuideView等等,有需要構建彈窗的時候可以去檢視相關類。

(關於WKBaseView,是一個封裝的非常完善的彈窗基類,預計後續會寫一篇文章詳細介紹,它的整個結構是非常完善,針對彈窗功能幾乎沒有擴充瓶頸)

實現原理

白名單模式(功能點4)

白名單模式只有一個關鍵內容

白名單字典 - whiteList

白名單字典位於WKViewManager類中。

字典中key為從屬於WKBaseView的子類的類名字串,value是一個陣列,裡面放入對應的ViewController的類名字串。

例如 白名單內容為

@{@"KMAnnouncementView":@[@"HJTMainTabBarVC"]};

則在廣告公告模組中,KMAnnouncementView呼叫顯示時,所獲取的View不是HJTMainTabBarVC的View,則KMAnnouncementView會進入白名單待顯示陣列中,當HJTMainTabBarVC呼叫viewwillappear時才會實際被顯示出來。

一句話介紹白名單

白名單中的key對應的view永遠只會出現在其對應的value中的vc上,即便你在其他任何VC上呼叫顯示顯示KMAnnouncementView, KMAnnouncementView也只會出現在其對應的value的vc上。

黑名單模式(功能點5)

黑名單模式包括兩個關鍵內容。

 黑名單字典 - blackList
 黑名單HomeVC - blackListHomeVCClassName

黑名單字典位於WKViewManager類中。

字典中key為ViewController的類名字串,value是一個陣列,裡面應放入對應的從屬於WKBaseView的子類的類名字串。

例如 黑名單內容為

            
 @{@"KMBaseViewController",@[@"WKPopBaseView"]};

則表明,WKPopBaseView類(不包含其子類),如果由於功能塊解耦或者其它原因,導致WKPopBaseView被展示於KMBaseViewController(不包含其子類)的子檢視上時,WKViewManager會攔截此行為,將WKPopBaseView例項存入黑名單待顯示陣列,當回到黑名單HomeVC時再顯示出來。從而實現了WKPopBaseView一定不會出現在KMBaseViewController的檢視上。

一句話解釋黑名單

黑名單中key對應的vc顯示時,其對應的value中的view必然不會出現,如果被強制出現時,其也不會出現,直到黑名單homeVC的viewwillappear被呼叫才會顯示。

功能點6

由於佇列基於viewcontroller的,所以很簡單隻需要hook viewcontroller的 willappear和diddisappear即可,在willappear裡面找到佇列設定suspended為NO,在diddisappear裡面suspended為yes即可

Demo地址

使用方式

前提:因為內部實現使用了Masonry佈局,所以需要先Pod Masonry三方庫。

步驟一、

下載Demo,找到BaseView資料夾拖入專案。

步驟二、

將檔案中的WKViewManagerHeader.h檔案加入全域性標頭檔案.pch中。

步驟三、

新建的View繼承自WKPopBaseView,並重寫setInterFace,需首先呼叫父類的實現

- (void)setInterFace
{
    [super setInterFace];
    //TODO contentview的高度必須設定
    [self.contentView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(self);
        make.centerY.equalTo(self).offset(SCREEN_HEIGHT);
        make.width.height.equalTo(@200);
    }];
}

可自定義出現和消失動畫——–實現updateContentViewConstraint:(BOOL)isShow方法

- (void)updateContentViewConstraint:(BOOL)isShow
{
    [self.contentView mas_updateConstraints:^(MASConstraintMaker *make) {
        make.centerY.equalTo(self).offset(isShow ? 0 : SCREEN_HEIGHT);
    }];
}

還有更多可自定義的效果,可通過頂層基類WKbaseVIew封裝的宣告週期實現。詳情請檢視Demo

步驟四、

初始化後,在需要展示的地方呼叫showInView:isShow:方法即可。

    WKTestView * v = [[WKTestView alloc] init];
    v.title = @"This is Test View can not show in HomePage";
    [v showInView:self.view isShow:YES];

交流方式

郵箱357863248@qq.com

相關文章