runtime入侵實現tableview自動顯示隱藏無資料佔位圖
在我們開發中,tableview和collectionview經常被用來展示資料列表。一般來說,tableView獲取到的資料來源為空時,直接展示一個空的tableView顯得比較突兀,所以設計師往往會針對這種情況給出相應的UI,用來替代空tableView的展示。無資料佔位圖,就是當後臺返回的資料來源為空時需要展示的頁面
如果只是想實現這個效果,有非常多的方法。
下面介紹一種利用runtime methodchange來實現自動控制的方法,使用非常簡單便捷,只需要import檔案,進行屬性控制就可以了。避免繁瑣的手動控制佔位圖顯示隱藏
使用示例:一般app的佔位檢視都是一致的 不需要每個tableview都去重複建立
建議佔位檢視根據專案情況單獨封裝一個uiview
或者把預設的view寫到分類程式碼裡邊兒,就不用呼叫下面的set方法了
//自動控制佔位圖 self.tableView.autoShowNoData = YES; //設定佔位圖 //一般app的佔位檢視都是一致的 不需要每個tableview都去重複建立 //建議佔位檢視根據專案情況單獨封裝一個uiview //或者把預設的view寫到分類程式碼裡邊兒,就不用呼叫下面的方法了 self.tableView.nodataView = ({ UILabel *label = [[UILabel alloc] init]; label.text = @"暫無內容"; label.textAlignment = NSTextAlignmentCenter; label.textColor = [UIColor blackColor]; label.backgroundColor = [UIColor groupTableViewBackgroundColor]; label; });
實現思路
1.佔位圖設定並儲存
2.提供展示和隱藏方法
3.攔截資料變化
4.判斷是否有資料自動呼叫展示隱藏方法
1.佔位圖設定並儲存
初始化一個檢視來做為佔位,這個很簡單,根據專案UI設計的來做就行了。那怎麼儲存呢,用tableview自身去儲存佔位圖物件,為什麼呢?因為如果這一步使用了其他的物件去儲存佔位圖,會導致程式碼耦合性增大。要做好一個封裝,儘量降低耦合,所有這裡不引入其他物件了。
tableview沒有佔位檢視這個屬性,那麼我們就給它動態增加一個nodataView
建立一個分類 增加屬性
@interface UITableView (NoData) //可定製的無資料檢視 預設為顯示暫無資料label //居中顯示 @property(nonatomic,strong)UIView*nodataView; //自動顯示隱藏無資料檢視 根據協議返回的section個數以及cell個數聯合判斷 (預設為NO,需要手動呼叫showNoDataView顯示) @property(nonatomic,assign)BOOLautoShowNoData; @end
.m檔案實現setter gtter
- (UIView *)nodataView { return objc_getAssociatedObject(self, @selector(nodataView)); } - (void)setNodataView:(UIView *)nodataView{ objc_setAssociatedObject(self, @selector(nodataView), nodataView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void)setAutoShowNoData:(BOOL)autoShowNoData{ objc_setAssociatedObject(self, @selector(autoShowNoData), @(autoShowNoData), OBJC_ASSOCIATION_RETAIN_NONATOMIC); [self reloadData]; } - (BOOL)autoShowNoData{ return [objc_getAssociatedObject(self, @selector(autoShowNoData)) boolValue]; }
2.提供展示和隱藏方法
提供方法並配置預設佔位檢視 預設佔位圖根據專案情況設定,這個很重要,可以減少業務層程式碼
- (void)showNoDataView { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self removeNoDataView]; if (!self.nodataView) { UILabel *label = [[UILabel alloc] init]; label.text = @"暫無內容"; label.textAlignment = NSTextAlignmentCenter; label.textColor = [UIColor hexColor:@"363636"]; self.nodataView = label; } [self addSubview:self.nodataView]; [self.nodataView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(@0); make.centerY.equalTo(@0); }]; }); } - (void)removeNoDataView { [self.nodataView removeFromSuperview]; }
3.攔截資料變化事件
如何攔截到每一次資料更新?由於tableview的資料顯示變化都是由reloadData方法導致的,所以我們想辦法監聽這個方法的執行。可以用切面程式設計的思想,Aspects這個框架可以做到,非常好用。mjrefresh的mj_reloadBlock也可以做到,但是我們要儘量減少依賴,這裡我們可以使用 <objc/runtime.h>的method_exchangeImplementations函式進行方法替換
method_exchangeImplementations 攔截reloadData
+ (void)load{ Method reloadData= class_getInstanceMethod(self, @selector(reloadData)); Method wy_reloadData = class_getInstanceMethod(self, @selector(wy_reloadData)); method_exchangeImplementations(reloadData, wy_reloadData); } - (void)wy_reloadData{ [self wy_reloadData]; [self checkData]; }
4.判斷是否有資料自動呼叫展示隱藏方法
通過上面的步驟我們成功的攔截到了每一次資料變化,接下來就是判斷是否有資料了。
- (void)checkData{ if (self.autoShowNoData) { BOOL haveData = YES; if (!self.dataSource) { haveData = NO; }else{ if ([self.dataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]&&[self.dataSource numberOfSectionsInCollectionView:self]<=0) { haveData = NO; }else{ NSInteger section =[self.dataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]? [self.dataSource numberOfSectionsInCollectionView:self]:1; haveData = NO; for (int i = 0; i<section; i++) { if ([self.dataSource collectionView:self numberOfItemsInSection:i]>0) { haveData = YES; break; } } } } if (haveData) { [self removeNoDataView]; }else{ [self showNoDataView]; } } }
這樣就完成了tableview的佔位控制分類封裝。
使用起來是不是很方便啊