UITableView占位圖的低耦合性設(shè)計

緣由

基于面向?qū)ο蟮拈_發(fā)原則中的迪米特法則:一個軟件實體應(yīng)當盡可能少的與其他實體發(fā)生相互作用;為了降低列表無數(shù)據(jù)占位圖的使用成本及代碼耦合性,對網(wǎng)上現(xiàn)用的一些解決方案加以優(yōu)化;

核心

針對基于runtime替換reloadData方法的相關(guān),這里就不做多闡述了,本文主要討論以下幾個問題:

  • 1.需要顯示占位圖的情況;
  • 2.tableView初次系統(tǒng)調(diào)用reloadData方法的干擾排除最優(yōu)方案;
  • 3.網(wǎng)絡(luò)因服務(wù)器故障請求失敗的處理;
  • 4.占位圖觸發(fā)再次網(wǎng)絡(luò)請求的策略;

問題1:需要顯示占位圖的情況

現(xiàn)在流行的判斷方案是:

  • tableView.rows==0;

我需要補充說明的是:

  • tableView.sections>0&&tableView.rows==0&&tableView.viewForHeaderInSection!=nil;

針對第一種rows==0的情況就不做多解釋;第二種的話主要就是:當一個列表的數(shù)據(jù)綁定在sectionHeaderView上面,此時row==0;然后需求是:點擊sectionHeaderView,展開section,刷新數(shù)據(jù);row>=0;所以如果僅僅考慮rows==0的情況,在第二種需求的情況占位圖顯示就會異常;

補充:無網(wǎng)絡(luò)的時候直接加載占位圖;

問題2:tableView初次系統(tǒng)調(diào)用reloadData方法的干擾排除最優(yōu)方案

在網(wǎng)上我看到的解決方案是:
在category給UITableView新增isFirstReload屬性;如果是第一次加載的話設(shè)置tableView.isFirstReload = YES;然后內(nèi)部的判斷是:

if (!self.firstReload) {
    [self checkEmpty];
}
self.firstReload = NO;

針對每次都需要在控制器中調(diào)用tableView.isFirstReload = YES,我也是做了很多優(yōu)化,比如最開始的時候我會想直接在基類viewDidLoad或者利用Aspects切入viewWillApear``方法中:遍歷子視圖,如果是[UITableView Class]或者[UICollectionView Class]就直接調(diào)用;

- (void)aspectViewWillAppearWithViewController:(UIViewController *)viewController
{
    NSArray *subViews = viewController.view.subviews;
    [subViews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([view isKindOfClass:[UITableView class]]) {
            UITableView *tableView = (UITableView *)view;
            if (!tableView.isNotFirstReload) {
                tableView.isNotFirstReload = NO;
            }
        }
        //如果tableView非self.view的直接子視圖,而是孫視圖....  
        //可用遞歸優(yōu)化;
        NSArray *secondLevelSubviews = view.subviews;
        [secondLevelSubviews enumerateObjectsUsingBlock:^(UIView *secondView, NSUInteger idx, BOOL * _Nonnull stop) {
            if ([secondView isKindOfClass:[UITableView class]]) {
                UITableView *tableView = (UITableView *)secondView;
                if (!tableView.isNotFirstReload) {
                    tableView.isNotFirstReload = NO;
                }
            }
            NSArray *thirdLevelSubviews = secondView.subviews;
            [thirdLevelSubviews enumerateObjectsUsingBlock:^(UIView *thirdView, NSUInteger idx, BOOL * _Nonnull stop) {
                if ([thirdView isKindOfClass:[UITableView class]]) {
                    UITableView *tableView = (UITableView *)thirdView;
                    if (!tableView.isNotFirstReload) {
                        tableView.isNotFirstReload = NO;
                    }
                }
            }];
        }];
    }];
}

如此優(yōu)化,是可以達到效果,但是在視圖啟動的時候遍歷子視圖無非是性能耗損的;最后腦筋急轉(zhuǎn)彎,其實也就是一個很簡單的方法就能解決這個問題:

@property (nonatomic, assign) BOOL isNotFirstReload;  
  • if (self.isNotFirstReload) {
    [self checkEmpty];
    }
    self.isNotFirstReload = YES;

BOOL屬性第一次加載的時候本來就是NO,也就避免了外部的傳入;

問題3:網(wǎng)絡(luò)因服務(wù)器故障請求失敗的處理

也就是在網(wǎng)絡(luò)請求的時候走failure的時候;一般情況下,在控制器失敗的回調(diào)中我們不會手動調(diào)用[self.taleView reloadData];如果不調(diào)用的話,就不能正確的加載占位圖了;當然你也可以在失敗的回調(diào)中調(diào)用reloadData方法解決這個問題;我這里給出另外一種解決方案:

通過windowrootViewController拿到當前的控制器,然后通過遍歷當前控制器的子視圖獲取tableView,調(diào)用reloadData方法,主要代碼如下:

    + (instancetype)shareInstance
{
    static RequestFailureHandler *shareInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        shareInstance = [[RequestFailureHandler alloc] init];
    });
    return shareInstance;
}
- (void)handleRequestFailure
{
    //根控制器是UINavigationController
    if ([self.rootVC isKindOfClass:[UINavigationController class]]) {
        [[RequestFailureHandler shareInstance] handleWithNavgationController:(UINavigationController *)self.rootVC];
    }else
    {
        //沒有UINavigationController的情況下
        [[RequestFailureHandler shareInstance] findTargetViewWithController:self.rootVC];
    }
}
- (void)handleWithNavgationController:(UINavigationController *)nav
{
    UIViewController *vc = nav.visibleViewController;
    if (vc.childViewControllers.count>0) {
        
        if ([vc.childViewControllers.firstObject isKindOfClass:[UIPageViewController class]]) {
            UIPageViewController *pageVc = (UIPageViewController *)vc.childViewControllers.firstObject;
            UIViewController *pageChild = pageVc.viewControllers.firstObject;
            [[RequestFailureHandler shareInstance] findTargetViewWithController:pageChild];
        }
    }else{
        [[RequestFailureHandler shareInstance] findTargetViewWithController:vc];
    }
}
- (void)findTargetViewWithController:(UIViewController *)viewController
{
    NSArray *subViews = viewController.view.subviews;
    [subViews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([view isKindOfClass:[UITableView class]]) {
            UITableView *tableView = (UITableView *)view;
            [tableView reloadData];
        }
    }];
}
#pragma mark - Getters & Setters
- (UIViewController *)rootVC
{
    if (!_rootVC) {
        _rootVC = [[[UIApplication sharedApplication] delegate] window].rootViewController;
    }
    return _rootVC;
}

針對這個做法,子視圖的遍歷,性能是會耗損的;但是考慮到這個主要是請求失敗的回掉中(不像在問題2中是在控制器啟動的時候);耗損也不會影響其他,并且能夠統(tǒng)一處理;所以湊合能用;

補充:后面突然這個方案存在一個問題:那就是當一個界面存在多個請求的時候,其中任何一個請求失敗會干擾占位圖的加載;暫時沒想到更好的解決辦法;

問題4:占位圖觸發(fā)再次網(wǎng)絡(luò)請求的策略

事件回調(diào):

  • block回調(diào)
  • delegate回調(diào)

可以直接在每個控制器中接收回調(diào),并完成再次請求;我在這里想的在基類懶加載tableView對象,然后設(shè)置代理接收回調(diào);在回調(diào)里面調(diào)用網(wǎng)絡(luò)請求的統(tǒng)一方法;

- (void)loadData
{
   //子類重寫這個方法,并且在這個方法中進行網(wǎng)絡(luò)請求
}

#pragma mark - ReRequesDataDelegate
- (void)reRequesData
{
    [self loadData];
}

- (UITableView *)tableView
{
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
        _tableView.tableFooterView = [UIView new];
        _tableView.dataSource = self;
        _tableView.delegate = self;
        _tableView.reRequestDelegate = self;
    }
    return _tableView;
}   

這樣的話,子類只需要重寫loadData;并在里面執(zhí)行網(wǎng)絡(luò)請求,就可以達到目的;

github

更多精彩:

軟件化ESJsonFormat插件,脫離Xcode環(huán)境運行
iOS_K線三方庫

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市岩喷,隨后出現(xiàn)的幾起案子矢腻,更是在濱河造成了極大的恐慌嘱朽,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡叛薯,警方通過查閱死者的電腦和手機浑吟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耗溜,“玉大人组力,你說我怎么就攤上這事《端” “怎么了燎字?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長城舞。 經(jīng)常有香客問我轩触,道長寞酿,這世上最難降的妖魔是什么家夺? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮伐弹,結(jié)果婚禮上拉馋,老公的妹妹穿的比我還像新娘。我一直安慰自己惨好,他們只是感情好煌茴,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著日川,像睡著了一般蔓腐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上龄句,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天回论,我揣著相機與錄音,去河邊找鬼分歇。 笑死傀蓉,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的职抡。 我是一名探鬼主播葬燎,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼缚甩!你這毒婦竟也來了谱净?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤擅威,失蹤者是張志新(化名)和其女友劉穎岳遥,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體裕寨,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡浩蓉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年派继,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捻艳。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡驾窟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出认轨,到底是詐尸還是另有隱情绅络,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布嘁字,位于F島的核電站恩急,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏纪蜒。R本人自食惡果不足惜衷恭,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纯续。 院中可真熱鬧随珠,春花似錦、人聲如沸猬错。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽倦炒。三九已至显沈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逢唤,已是汗流浹背拉讯。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留智玻,地道東北人遂唧。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像吊奢,于是被迫代替她去往敵國和親盖彭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫页滚、插件召边、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,059評論 4 62
  • 從個人的角度來看: 今天我的心情怎么樣?有什么印象深刻的事情裹驰?什么事情讓我獲得了成就感隧熙?遇到了什么問題?有什么疑惑...
    落花的季節(jié)閱讀 171評論 2 2
  • 天是灰白色的 厚厚沉沉的感覺 是一張灰白的大塑料布 看不分明 雖有小雨 卻不是淅淅瀝瀝下下來 一粒粒的水珠兒在空中...
    shyaen閱讀 192評論 0 0
  • Ok! Let’s get start! 先讓我們問自己幾個問題: 1音念、你認為自己成功嗎? 2躏敢、你認為自己聰明嗎闷愤?...
    Miss宛笑閱讀 332評論 0 0
  • 三十而立的我,事業(yè)上算不上成功件余,家庭還算圓滿讥脐,只是一直在心里有一個夢,對啼器,一點都沒錯旬渠,那只是一個夢而已,沒有辦法實...
    圣西羅之紫彤閱讀 303評論 0 0