| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- //
- // UITableView+XY.m
- // XYTableViewNoDataView
- //
- // Created by 韩元旭 on 2017/7/19.
- // Copyright © 2017年 iCourt. All rights reserved.
- //
- #import "UITableView+XY.h"
- #import "XYNoDataView.h"
- #import <objc/runtime.h>
- /**
- 消除警告
- */
- @protocol XYTableViewDelegate <NSObject>
- @optional
- - (UIView *)xy_noDataView; // 完全自定义占位图
- - (UIImage *)xy_noDataViewImage; // 使用默认占位图, 提供一张图片, 可不提供, 默认不显示
- - (NSString *)xy_noDataViewMessage; // 使用默认占位图, 提供显示文字, 可不提供, 默认为暂无数据
- - (UIColor *)xy_noDataViewMessageColor; // 使用默认占位图, 提供显示文字颜色, 可不提供, 默认为灰色
- - (NSNumber *)xy_noDataViewCenterYOffset; // 使用默认占位图, CenterY 向下的偏移量
- @end
- @implementation UITableView (XY)
- /**
- 加载时, 交换方法
- */
- + (void)load {
-
- // 只交换一次
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
-
- Method reloadData = class_getInstanceMethod(self, @selector(reloadData));
- Method xy_reloadData = class_getInstanceMethod(self, @selector(xy_reloadData));
- method_exchangeImplementations(reloadData, xy_reloadData);
-
- Method dealloc = class_getInstanceMethod(self, NSSelectorFromString(@"dealloc"));
- Method xy_dealloc = class_getInstanceMethod(self, @selector(xy_dealloc));
- method_exchangeImplementations(dealloc, xy_dealloc);
- });
- }
- /**
- 在 ReloadData 的时候检查数据
- */
- - (void)xy_reloadData {
-
- [self xy_reloadData];
-
- // 忽略第一次加载
- if (![self isInitFinish]) {
- [self xy_havingData:YES];
- [self setIsInitFinish:YES];
- return ;
- }
- // 刷新完成之后检测数据量
- dispatch_async(dispatch_get_main_queue(), ^{
-
- NSInteger numberOfSections = [self numberOfSections];
- BOOL havingData = NO;
- for (NSInteger i = 0; i < numberOfSections; i++) {
- if ([self numberOfRowsInSection:i] > 0) {
- havingData = YES;
- break;
- }
- }
-
- [self xy_havingData:havingData];
- });
- }
- /**
- 展示占位图
- */
- - (void)xy_havingData:(BOOL)havingData {
-
- // 不需要显示占位图
- if (havingData || self.tag == 1107) {
- [self freeNoDataViewIfNeeded];
- self.backgroundView = nil;
- return ;
- }
-
- // 不需要重复创建
- if (self.backgroundView) {
- return ;
- }
-
- // 自定义了占位图
- if ([self.delegate respondsToSelector:@selector(xy_noDataView)]) {
- self.backgroundView = [self.delegate performSelector:@selector(xy_noDataView)];
- return ;
- }
-
- // 使用自带的
- UIImage *img = [UIImage imageNamed:@"暂无数据"];
- NSString *msg = @"亲~暂无任何内容";
- UIColor *color = [UIColor lightGrayColor];
- CGFloat offset = 0;
-
- // 获取图片
- if ([self.delegate respondsToSelector:@selector(xy_noDataViewImage)]) {
- img = [self.delegate performSelector:@selector(xy_noDataViewImage)];
- }
- // 获取文字
- if ([self.delegate respondsToSelector:@selector(xy_noDataViewMessage)]) {
- msg = [self.delegate performSelector:@selector(xy_noDataViewMessage)];
- }
- // 获取颜色
- if ([self.delegate respondsToSelector:@selector(xy_noDataViewMessageColor)]) {
- color = [self.delegate performSelector:@selector(xy_noDataViewMessageColor)];
- }
- // 获取偏移量
- if ([self.delegate respondsToSelector:@selector(xy_noDataViewCenterYOffset)]) {
- offset = [[self.delegate performSelector:@selector(xy_noDataViewCenterYOffset)] floatValue];
- }
-
- // 创建占位图
- self.backgroundView = [self xy_defaultNoDataViewWithImage :img message:msg color:color offsetY:offset];
- }
- /**
- 默认的占位图
- */
- - (UIView *)xy_defaultNoDataViewWithImage:(UIImage *)image message:(NSString *)message color:(UIColor *)color offsetY:(CGFloat)offset {
-
- // 计算位置, 垂直居中, 图片默认中心偏上.
- CGFloat sW = self.bounds.size.width;
- CGFloat cX = sW / 2;
- CGFloat cY = self.bounds.size.height * (1 - 0.618) + offset;
- CGFloat iW = image.size.width;
- CGFloat iH = image.size.height;
-
- // 图片
- UIImageView *imgView = [[UIImageView alloc] init];
- imgView.frame = CGRectMake(cX - iW / 2, cY - iH / 2, iW, iH);
- imgView.image = image;
-
- // 文字
- UILabel *label = [[UILabel alloc] init];
- label.font = [UIFont systemFontOfSize:17];
- label.textColor = color;
- label.text = message;
- label.textAlignment = NSTextAlignmentCenter;
- label.frame = CGRectMake(0, CGRectGetMaxY(imgView.frame) + 24, sW, label.font.lineHeight);
-
- // 视图
- XYNoDataView *view = [[XYNoDataView alloc] init];
- [view addSubview:imgView];
- [view addSubview:label];
-
- // 实现跟随 TableView 滚动
- [view addObserver:self forKeyPath:kXYNoDataViewObserveKeyPath options:NSKeyValueObservingOptionNew context:nil];
- return view;
- }
- /**
- 监听
- */
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
- if ([keyPath isEqualToString:kXYNoDataViewObserveKeyPath]) {
-
- /**
- 在 TableView 滚动 ContentOffset 改变时, 会同步改变 backgroundView 的 frame.origin.y
- 可以实现, backgroundView 位置相对于 TableView 不动, 但是我们希望
- backgroundView 跟随 TableView 的滚动而滚动, 只能强制设置 frame.origin.y 永远为 0
- 兼容 MJRefresh
- */
- CGRect frame = [[change objectForKey:NSKeyValueChangeNewKey] CGRectValue];
- if (frame.origin.y != 0) {
- frame.origin.y = 0;
- self.backgroundView.frame = frame;
- }
- }
- }
- #pragma mark - 属性
- /**
- 设置已经加载完成数据了
- */
- - (void)setIsInitFinish:(BOOL)finish {
- objc_setAssociatedObject(self, @selector(isInitFinish), @(finish), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- /**
- 是否已经加载完成数据
- */
- - (BOOL)isInitFinish {
- id obj = objc_getAssociatedObject(self, _cmd);
- return [obj boolValue];
- }
- /**
- 移除 KVO 监听
- */
- - (void)freeNoDataViewIfNeeded {
-
- if ([self.backgroundView isKindOfClass:[XYNoDataView class]]) {
- [self.backgroundView removeObserver:self forKeyPath:kXYNoDataViewObserveKeyPath context:nil];
- }
- }
- - (void)xy_dealloc {
- [self freeNoDataViewIfNeeded];
- [self xy_dealloc];
- NSLog(@"TableView + XY 视图正常销毁");
- }
- @end
|