本文為投稿文章,作者:賣報的小畫家Sure 原文
前文提要
近期准備重構項目,需要重寫一些通用模塊,正巧需要設置App異常加載占位圖的問題,心血來潮設想是否可以零行代碼解決此問題,特在此分享實現思路。
思路分享
對於App占位圖,通常需要考慮的控件有tableView、collectionView和webView,異常加載情況區分為無數據和網絡異常等。
既然要實現零代碼形式,因此就不能繼承原始類重寫或添加方法等方式,而是通過對對應控件添加類別(分類)來實現。
簡單來說,以tableView為例實現思路為每當tableView調用reloadData進行刷新時,檢測此時tableView行數,若行數不為零,正常顯示數據。若行數為零,說明無數據顯示占位圖。
添加占位圖的方式有很多種,例如借助tableView的backgroundView或直接以addSubView的方式添加,這裡采用的為addSubView方式,盡量避免原生屬性的占用。
對於檢測tableView數據是否為空,借助tableView的代理dataSource即可。核心代碼如下,依次獲取tableView所具有的組數與行數,通過isEmpty這個flag標示最後確定是否添加占位圖。
- (void)checkEmpty { BOOL isEmpty = YES;//flag標示 iddataSource = self.dataSource; NSInteger sections = 1;//默認一組 if ([dataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]) { sections = [dataSource numberOfSectionsInTableView:self] - 1;//獲取當前TableView組數 } for (NSInteger i = 0; i <= sections; i++) { NSInteger rows = [dataSource tableView:self numberOfRowsInSection:sections];//獲取當前TableView各組行數 if (rows) { isEmpty = NO;//若行數存在,不為空 } } if (isEmpty) {//若為空,加載占位圖 if (!self.placeholderView) {//若未自定義,展示默認占位圖 [self makeDefaultPlaceholderView]; } self.placeholderView.hidden = NO; [self addSubview:self.placeholderView]; } else {//不為空,隱藏占位圖 self.placeholderView.hidden = YES; } }
相應的對於CollectionView亦可通過numberOfSectionsInCollectionView:和collectionView:numberOfItemsInSection獲取其組數和行數,這裡就不一一贅述。
需要注意的為webView占位圖是否顯示的判斷,一種情況為webView調用其webView: didFailLoadWithError:方法,第二種為webView完成加載顯示為空的情況。但存在的一個問題是,webView沒有必選的協議方法,或可能根本沒有設置代理。因此無法很好的判斷webView是否響應其協議方法。因此該demo暫時沒有添加webView的占位圖,如果有好的想法可以評論指出。
接下來說最重要的一步,如何實現零行代碼添加占位圖呢?
其實實現思路非常簡單,如果可以讓tableView在執行reloadData時自動檢測其行數就可以了。也就是我們需要在reloadData原有方法的基礎上添加checkEmpty此方法。
這裡又能體現到Runtime Method Swizzling的作用了,我們可以通過Method Swizzling替換reloadData方法,給予它新的實現。核心代碼如下:
+ (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //方法交換,將reloadData實現交換為sure_reloadData [self methodSwizzlingWithOriginalSelector:@selector(reloadData) bySwizzledSelector:@selector(sure_reloadData)]; }); } - (void)sure_reloadData { [self checkEmpty]; [self sure_reloadData]; }
這樣就可以實現reloadData的同時檢測行數從而判斷是否顯示占位圖的功能。
這裡采用了上篇文章《Runtime Method Swizzling開發實例匯總》的代碼用例類NSObject+Swizzling.h,因此該篇文章也算上篇文章的延續,為Runtime Method Swizzling的另一種用例。感興趣的朋友可以前往閱讀更多的實用用例。
為實現零代碼的效果,代碼中已添加了placeholder視圖的默認樣式,如圖所示:
占位圖樣式
若要實現效果圖中點擊圖標重新刷新效果,需要讓tableView調用reloadBlock,因為數據的刷新大多是不同的,所以具體刷新執行代碼還是需要自己手動設置的。若不需要,則無需添加此操作。
[_tableView setReloadBlock:^{ //刷新操作 }];
如果需要自定制占位視圖樣式也非常簡單,因占位圖樣式比較統一,所以可直接修改SurePlaceholderView占位圖類以達到自己想要的效果,再而在UITableView+Sure_Placeholder.h、UICollectionView+Sure_Placeholder.h、UIWebView+Sure_Placeholder.h類別中均外漏了placeholderView屬性,將其賦值為新的視圖亦可。
以tableView為例,可以通過如下方式進行修改
_tableView.placeholderView =[[CustomPlaceholderView alloc]initWithFrame:_tableView.bounds];
同樣的對於無數據與無網絡的效果切換,也可以通過網絡是否可用的標示來進行展示不同的占位圖。例如
if (is_Net_Available) { _tableView.placeholderView = [[CustomPlaceholderView alloc]initWithFrame:_tableView.bounds]; } else { _tableView.placeholderView = [[NetNoAvailableView alloc]initWithFrame:_tableView.bounds]; }
為方便大家閱讀和修改,demo已上傳github。
下載鏈接如下:零行代碼為App添加無數據占位圖????
既然為零代碼,因此使用方法將Sure_Placeholder文件夾拖入工程即可。有任何問題大家可以評論指出。
參考鏈接:《一行代碼完成“空TableView占位視圖”管理》