IU
这是要封装的
这里展示的仅仅是tableView的占位图。
封装原因
最近重构分类详情页的时候需要实现这个功能,关于这个view我已经封装过几次了,今天我试着回想当初的思路,竟然没什么印象了!对于自己封装过的东西没什么印象原因只有一种:不够简洁。
此事无关代码,关乎优雅
然后我翻看了一下自己以前的写的关于封装这个view的简书,不得不说,挺搓的。。。(每次回头看曾经的代码,都不甚满意。。。)
曾经的问题
首先,在基类里实现这个功能就是变相的提升程序的耦合度:我的目的是给tableView添加一个功能,并且是纯粹的添加功能,不需要新类,所以这种情况应该用category。想想MJRefresh,使用多么简单方便,用过一次就难以忘记。
连逻辑都繁琐不堪
这是使用方法:
// 展示无数据占位图 [self.tableView showEmptyViewWithType:NoContentTypeNetwork]; // 无数据占位图点击的回调 self.tableView.noContentViewTapedBlock = ^{ [SVProgressHUD showSuccessWithStatus:@"没网"]; }; 3 // 移除无数据占位图 [self.tableView removeEmptyView];
如此不堪的使用方法,甚至还要手动移除占位图。
优化
之前只是针对UITableView占位图的封装,这次面向UIView。
曾经的问题就是现在要解决的问题,在这之前,先将思路理一理:
封装这个view:
至少需要一个参数type来表示是哪种类型的占位图:没网or空数据等等;
需要一个回调来处理用户点击重新加载按钮事件。
每次展示的占位图只可能有一个。
代码
这是基于UIView的category:
@interface UIView () /** 占位图 */ @property (nonatomic, strong) UIView *cq_placeholderView; @end @implementation UIView (PlaceholderView) static void *strKey = &strKey; - (UIView *)cq_placeholderView { return objc_getAssociatedObject(self, &strKey); } - (void)setCq_placeholderView:(UIView *)cq_placeholderView { objc_setAssociatedObject(self, &strKey, cq_placeholderView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } /** 展示UIView及其子类的占位图 @param type 占位图类型 @param reloadBlock 重新加载回调的block */ - (void)cq_showPlaceholderViewWithType:(CQPlaceholderViewType)type reloadBlock:(void (^)())reloadBlock { // 如果是UIScrollView及其子类,占位图展示期间禁止scroll BOOL originalScrollEnabled = NO; // 原本的scrollEnabled if ([self isKindOfClass:[UIScrollView class]]) { UIScrollView *scrollView = (UIScrollView *)self; // 先记录原本的scrollEnabled originalScrollEnabled = scrollView.scrollEnabled; // 再将scrollEnabled设为NO scrollView.scrollEnabled = NO; } //------- 占位图 -------// if (self.cq_placeholderView) { [self.cq_placeholderView removeFromSuperview]; self.cq_placeholderView = nil; } self.cq_placeholderView = [[UIView alloc] init]; [self addSubview:self.cq_placeholderView]; self.cq_placeholderView.backgroundColor = [UIColor whiteColor]; [self.cq_placeholderView mas_makeConstraints:^(MASConstraintMaker *make) { make.center.mas_equalTo(self); make.size.mas_equalTo(self); }]; //------- 图标 -------// UIImageView *imageView = [[UIImageView alloc] init]; [self.cq_placeholderView addSubview:imageView]; [imageView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.mas_equalTo(imageView.superview); make.centerY.mas_equalTo(imageView.superview).mas_offset(-80); make.size.mas_equalTo(CGSizeMake(70, 70)); }]; //------- 描述 -------// UILabel *descLabel = [[UILabel alloc] init]; [self.cq_placeholderView addSubview:descLabel]; [descLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.mas_equalTo(descLabel.superview); make.top.mas_equalTo(imageView.mas_bottom).mas_offset(20); make.height.mas_equalTo(15); }]; //------- 重新加载button -------// UIButton *reloadButton = [[UIButton alloc] init]; [self.cq_placeholderView addSubview:reloadButton]; [reloadButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [reloadButton setTitle:@"重新加载" forState:UIControlStateNormal]; reloadButton.layer.borderWidth = 1; reloadButton.layer.borderColor = [UIColor blackColor].CGColor; [[reloadButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) { // 执行block回调 if (reloadBlock) { reloadBlock(); } // 从父视图移除 [self.cq_placeholderView removeFromSuperview]; self.cq_placeholderView = nil; // 复原UIScrollView的scrollEnabled if ([self isKindOfClass:[UIScrollView class]]) { UIScrollView *scrollView = (UIScrollView *)self; scrollView.scrollEnabled = originalScrollEnabled; } }]; [reloadButton mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.mas_equalTo(reloadButton.superview); make.top.mas_equalTo(descLabel.mas_bottom).mas_offset(20); make.size.mas_equalTo(CGSizeMake(120, 30)); }]; //------- 根据type设置不同UI -------// switch (type) { case CQPlaceholderViewTypeNoNetwork: // 网络不好 { NSString *path = [[NSBundle mainBundle] pathForResource:@"无网" ofType:@"png"]; imageView.image = [UIImage imageWithContentsOfFile:path]; descLabel.text = @"网络异常"; } break; case CQPlaceholderViewTypeNoGoods: // 没商品 { NSString *path = [[NSBundle mainBundle] pathForResource:@"无商品" ofType:@"png"]; imageView.image = [UIImage imageWithContentsOfFile:path]; descLabel.text = @"一个商品都没有"; } break; case CQPlaceholderViewTypeNoComment: // 没评论 { NSString *path = [[NSBundle mainBundle] pathForResource:@"沙发" ofType:@"png"]; imageView.image = [UIImage imageWithContentsOfFile:path]; descLabel.text = @"抢沙发!"; } break; default: break; } } @end
使用方法
需要展示占位图的时候直接调用方法:
[self.tableView cq_showPlaceholderViewWithType:CQPlaceholderViewTypeNoComment reloadBlock:^{ // 按钮点击回调 [SVProgressHUD showInfoWithStatus:@"重新加载按钮点击"]; }];
细节
1. 对于UIScrollView及其子类,占位图展示期间将它的scrollEnabled设置为NO。
这肯定不是我们想要的效果.gif
但是改变scrollEnabled的时候又不能对原控件造成侵入性,怎么破?
先记录最初的scrollEnabled:
// 如果是UIScrollView及其子类,占位图展示期间禁止scroll BOOL originalScrollEnabled = NO; // 原本的scrollEnabled if ([self isKindOfClass:[UIScrollView class]]) { UIScrollView *scrollView = (UIScrollView *)self; // 先记录原本的scrollEnabled originalScrollEnabled = scrollView.scrollEnabled; // 再将scrollEnabled设为NO scrollView.scrollEnabled = NO; }
移除占位图的时候复原UIScrollView的scrollEnabled:
// 复原UIScrollView的scrollEnabled if ([self isKindOfClass:[UIScrollView class]]) { UIScrollView *scrollView = (UIScrollView *)self; scrollView.scrollEnabled = originalScrollEnabled; }
2. 给系统类扩展方法或添加属性都要加上前缀,向SDWebImage学习。
// SDWebImage的方法 [imageView sd_setImageWithURL:nil completed:nil];
3. 图片的加载
不需要一直放在内存里的用imageWithContentsofFile:方法。
4. block
不需要写weakSelf:
[self.view cq_showPlaceholderViewWithType:CQPlaceholderViewTypeNoNetwork reloadBlock:^{ [SVProgressHUD showSuccessWithStatus:@"有网了"]; // 直接写self也不会导致内存泄漏 self.view.backgroundColor = [UIColor redColor]; }];
这里的block是局部变量,跟masonry的block是同一个道理:
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block { self.translatesAutoresizingMaskIntoConstraints = NO; MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; block(constraintMaker); return [constraintMaker install]; }
总结
只有不断的反思和总结才能造出更优雅的轮子。
作者:无夜之星辰
链接:http://www.jianshu.com/p/dccf16239ede
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。