背景
在APP中列表是一种比较常见的数据展示方式,当有数据时,就显示数据;如果没有数据,一般不会显示一个空白页面,而是在空白页面上加一些提示信息,比如像下面这样:
不同的APP会有不同的设计,但不管是什么样的设计,它在整个APP内部应该是一致的,要变也只是文字或图片稍有不同。
现状
因为我们目前的项目还算比较庞大,所以这种列表无数据的情况出现了20多次,所以类似下面的代码出现了就有20多次。为什么说类似,因为是由不同的人写的,逻辑也是差不多,但真的各不相同,有的封装成一个方法,比如:setNoMessageView,有的直接写在viewDidLoad里面......
- (void)setNoMessageView { self.noInfoView = [[UIView alloc] initWithFrame:CGRectMake(0, 45, SCREEN_WIDTH, SCREEN_HEIGHT)]; self.noInfoView.backgroundColor = [UIColor clearColor]; [self.view addSubview:self.noInfoView]; UIImageView *carImageView = [[UIImageView alloc] initWithFrame:CGRectMake((SCREEN_WIDTH-120)/2, 60, 120, 86)]; [carImageView setImage:[UIImage imageNamed:@"no_message.png"]]; [self.noInfoView addSubview:carImageView]; UILabel *noInfoLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 160, SCREEN_WIDTH, 20)]; noInfoLabel.textAlignment = NSTextAlignmentCenter; noInfoLabel.textColor = LCRGBColor(211, 211, 211); noInfoLabel.text = NSLocalizedString(@"Dear, no information", nil); noInfoLabel.backgroundColor = [UIColor clearColor]; noInfoLabel.font = [LCFont systemFontOfSize:20]; [self.noInfoView addSubview:noInfoLabel]; }
先不考虑重复的问题,只是孤立的看上述代码,它也有一些问题:
- self.noInfoView的frame应该视根据上下文获得的,而不是和屏幕大小绑定,而且yOffset是45也是不对的。
- carImageView的frame是固定大小的,而图片有可能变。
第一个解决办法
因为创建noInfoView的代码基本差不多,我们可以封装出一个Util方法。
+ (UIView*)createNoMessageViewWithFrame:(CGRect)frame image:(UIImage*)image text:(NSString*)text { UIView* noMessageView = [[UIView alloc] initWithFrame:frame]; noMessageView.backgroundColor = [UIColor clearColor]; UIImageView *carImageView = [[UIImageView alloc] initWithFrame:CGRectMake((frame.size.width-image.size.width)/2, 60, image.size.width, image.size.height)]; [carImageView setImage:image]; [noMessageView addSubview:carImageView]; UILabel *noInfoLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 160, frame.size.width, 20)]; noInfoLabel.textAlignment = NSTextAlignmentCenter; noInfoLabel.textColor = LCRGBColor(211, 211, 211); noInfoLabel.text = text; noInfoLabel.backgroundColor = [UIColor clearColor]; noInfoLabel.font = [LCFont systemFontOfSize:20]; [noMessageView addSubview:noInfoLabel]; return noMessageView; }
调用:
- (void)setNoMessageView { CGRect rect = self.shopListView.frame; UIImage* image = [UIImage imageNamed:@"no_message.png"]; NSString* text = NSLocalizedString(@"Dear, no information", nil); self.noInfoView = [HJUIUtil createNoMessageViewWithFrame:rect image:image text:text]; [self.view addSubview:self.noInfoView]; }
这样改看起来好多了,把共性封装,把差异作为接口留出。然后其他地方原本要写20行代码,现在只要写5行,而且多个地方调用的代码不会有太大的出入,便于阅读和理解。
第二个解决办法
上面的办法已经不错了,不过除了写5行代码之外,还给ViewController增加了一个属性:noInfoView。现在仔细想一下noInfoView出现的原因,是因为TableView没有内容显示的时候,noInfoView才显示出来,否则就隐藏。可见,这个noInfoView和TableView是紧密联系的,我们可以从UITableView的状态得出noInfoView的状态。那为什么不把它绑定到UITableView上呢?好,那就给UITableView增加一个属性,叫做emptyView。
给一个系统的类增加属性不是那么的简单,但是只要看过SVPullToRefresh的代码,就知道怎么做了。
首先,给UITableView增加一个Category。
@interface UITableView(EmptyView) @property (nonatomic, strong, readonly) UIView *emptyView; -(void)addEmptyViewWithImageName:(NSString*)imageName title:(NSString*)title; @end
static char UITableViewEmptyView; @implementation UITableView(EmptyView) @dynamic emptyView; - (UIView *)emptyView { return objc_getAssociatedObject(self, &UITableViewEmptyView); } - (void)setEmptyView:(UIView *)emptyView { [self willChangeValueForKey:@"HJEmptyView"]; objc_setAssociatedObject(self, &UITableViewEmptyView, emptyView, OBJC_ASSOCIATION_ASSIGN); [self didChangeValueForKey:@"HJEmptyView"]; } -(void)addEmptyViewWithImageName:(NSString*)imageName title:(NSString*)title { if (!self.emptyView) { CGRect frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height); UIImage* image = [UIImage imageNamed:imageName]; NSString* text = title; UIView* noMessageView = [[UIView alloc] initWithFrame:frame]; noMessageView.backgroundColor = [UIColor clearColor]; UIImageView *carImageView = [[UIImageView alloc] initWithFrame:CGRectMake((frame.size.width-image.size.width)/2, 60, image.size.width, image.size.height)]; [carImageView setImage:image]; [noMessageView addSubview:carImageView]; UILabel *noInfoLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 160, frame.size.width, 20)]; noInfoLabel.textAlignment = NSTextAlignmentCenter; noInfoLabel.textColor = LCRGBColor(211, 211, 211); noInfoLabel.text = text; noInfoLabel.backgroundColor = [UIColor clearColor]; noInfoLabel.font = [LCFont systemFontOfSize:20]; [noMessageView addSubview:noInfoLabel]; [self addSubview:noMessageView]; self.emptyView = noMessageView; } } @end
然后外部调用就很简单了,没有额外的属性,而且一句话就搞定。
[self.shopListView.shopListTableView addEmptyViewWithImageName:@"no_message.png" title:@"Dear, no information"];
然后在加载数据前进行判定是否显示emptyView
self.shopListView.shopListTableView.emptyView.hidden = ([self.dataArray count] != 0);