前文提要
近期準備重構專案,需要重寫一些通用模組,正巧需要設定App異常載入佔點陣圖的問題,心血來潮設想是否可以零行程式碼解決此問題,特在此分享實現思路。
思路分享
對於App佔點陣圖,通常需要考慮的控制元件有tableView、collectionView和webView,異常載入情況區分為無資料和網路異常等。
既然要實現零程式碼形式,因此就不能繼承原始類重寫或新增方法等方式,而是通過對對應控制元件新增類別(分類)來實現。
簡單來說,以tableView為例實現思路為每當tableView呼叫reloadData進行重新整理時,檢測此時tableView行數,若行數不為零,正常顯示資料。若行數為零,說明無資料顯示佔點陣圖。
新增佔點陣圖的方式有很多種,例如藉助tableView的backgroundView或直接以addSubView的方式新增,這裡採用的為addSubView方式,儘量避免原生屬性的佔用。
對於檢測tableView資料是否為空,藉助tableView的代理dataSource即可。核心程式碼如下,依次獲取tableView所具有的組數與行數,通過isEmpty這個flag標示最後確定是否新增佔點陣圖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
- (void)checkEmpty { BOOL isEmpty = YES;//flag標示 id <UITableViewDataSource> dataSource = 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方法,給予它新的實現。核心程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
+ (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,因為資料的重新整理大多是不同的,所以具體重新整理執行程式碼還是需要自己手動設定的。若不需要,則無需新增此操作。
1 2 3 |
[_tableView setReloadBlock:^{ //重新整理操作 }]; |
如果需要自定製佔位檢視樣式也非常簡單,因佔點陣圖樣式比較統一,所以可直接修改SurePlaceholderView
佔點陣圖類以達到自己想要的效果,再而在UITableView+Sure_Placeholder.h
、UICollectionView+Sure_Placeholder.h
、UIWebView+Sure_Placeholder.h
類別中均外漏了placeholderView屬性,將其賦值為新的檢視亦可。
以tableView為例,可以通過如下方式進行修改
1 |
_tableView.placeholderView =[[CustomPlaceholderView alloc]initWithFrame:_tableView.bounds]; |
同樣的對於無資料與無網路的效果切換,也可以通過網路是否可用的標示來進行展示不同的佔點陣圖。例如
1 2 3 4 5 |
if (is_Net_Available) { _tableView.placeholderView = [[CustomPlaceholderView alloc]initWithFrame:_tableView.bounds]; } else { _tableView.placeholderView = [[NetNoAvailableView alloc]initWithFrame:_tableView.bounds]; } |
為方便大家閱讀和修改,demo已上傳github。
下載連結如下:
零行程式碼為App新增無資料佔點陣圖?
既然為零程式碼,因此使用方法將Sure_Placeholder資料夾拖入工程即可。有任何問題大家可以評論指出。