DZNEmptyDataSet——空白数据集显示框架
DZNEmptyDataSet
DZNEmptyDataSet是基于UITableView/UICollectionView的范畴/扩展(category)类,它可以在空白页面上显示提示信息。
这是 iOS 内建的标准,用于处理空表和集合视图。默认情况下,如果你的表视图是空的,屏幕上什么也不会显示,它给用户的体验不是很好。
使用这个库,你只需要实现一些协议,iOS 就会很好地处理集合视图,然后合理美观地显示出用户信息。
支付宝-查询我的挂号记录
其他效果图参考
Mobile Patterns- Empty Data Sets
使用该框架的项目
将你的项目添加到列表中并且提供一张(320px)的效果图。
空数据设计模式(The Empty Data Set Pattern)
也被称为Empty state或者Blank Slate。
大多数应用程序会显示内容列表、数据集(在 iOS 程序猿眼里,这里通常指的是UITableView、UICollectionView。),但有些时候这些页面可能是空白的,特别是对于那些刚创建空账户的新用户来说。 空白界面会对用户造成不知道如何进行下一步操作的困惑,因为用户不知道屏幕空白的原因是错误/Bug、网络异常,还是用户应该自己新建内容以恢复APP的正常状态。
请阅读这篇非常有趣的文章Designing For The Empty States。
在iOS 9人机界面指南 1.4.2中也提及过类似的指引:
如果应用中所有的功能当前都不可用,那么应该显示一些内容来解释当前的情形,并建议用户如何进行后续操作。这部分内容给予了用户以反馈,使用户相信你的应用现在没问题。同时这也可以稳定用户情绪,让他们决定是否要采取纠正措施,继续使用应用,还是切换到另一个应用。
Empty Data Sets有助于:
避免使用空白屏幕,并向用户传达屏幕空白的原因。
用户指引(特别是作为引导页面)。
避免其他中断机制,如显示错误警报。
一致性和改善用户体验。
传递品牌价值。
特性
兼容UITableView和UICollectionView。 也兼容UISearchDisplayController和UIScrollView。
通过显示图片、标题、详细文本、按钮,提供布局外观的多种可能性。
使用NSAttributedString类提供更容易定制的外观。
使用Auto Layout以自动将内容集中到表格视图,并支持自动旋转。 也接受自定义垂直和水平对齐。
自定义背景颜色。
允许在整个表格矩形上轻敲手势(有助于放弃第一个响应者或类似操作)。
提供更高级的定制,允许自定义视图。
兼容Storyboard。
兼容iOS 6,tvOS 9或更高版本。
兼容iPhone,iPad和Apple TV。
支持 App Store 。
这个库已经被设计为不需要通过扩展(extend)UITableView或UICollectionView类的方式来实现了。 使用UITableViewController或UICollectionViewController类仍然可以奏效。 只要通过遵循DZNEmptyDataSetSource和DZNEmptyDataSetDelegate协议,您将能够完全自定义应用程序的空状态的内容和外观。
安装
支持CocoaPods导入
pod'DZNEmptyDataSet'
使用
完整文档参考:CocoaPods 自动生成文档
导入
#import"UIScrollView+EmptyDataSet.h"
作为框架导入:
#import
遵循协议
// 遵守 DZNEmptyDataSetSource 、DZNEmptyDataSetDelegate 协议@interfaceMainViewController:UITableViewController- (void)viewDidLoad{ [superviewDidLoad];self.tableView.emptyDataSetSource =self;self.tableView.emptyDataSetDelegate =self;// 删除单元格分隔线的一个小技巧self.tableView.tableFooterView = [UIViewnew];}
实现数据源协议
DZNEmptyDataSetSource ——实现该协议,可以设置你想要在空白页面显示的内容,并且充分利用NSAttributedString功能来自定义文本外观。
空白页显示图片
- (UIImage*)imageForEmptyDataSet:(UIScrollView*)scrollView {return[UIImageimageNamed:@"lion"];}
效果图:
空白页显示标题
- (NSAttributedString*)titleForEmptyDataSet:(UIScrollView*)scrollView {NSString*title =@"狮子王";NSDictionary*attributes = @{NSFontAttributeName:[UIFontboldSystemFontOfSize:18.0f],NSForegroundColorAttributeName:[UIColordarkGrayColor] };return[[NSAttributedStringalloc] initWithString:title attributes:attributes];}
效果图:
空白页显示详细描述
- (NSAttributedString*)descriptionForEmptyDataSet:(UIScrollView*)scrollView {NSString*text =@"你好,我的名字叫辛巴,大草原是我的家!";NSMutableParagraphStyle*paragraph = [NSMutableParagraphStylenew]; paragraph.lineBreakMode =NSLineBreakByWordWrapping; paragraph.alignment =NSTextAlignmentCenter;NSDictionary*attributes = @{NSFontAttributeName:[UIFontsystemFontOfSize:14.0f],NSForegroundColorAttributeName:[UIColorlightGrayColor],NSParagraphStyleAttributeName:paragraph };return[[NSAttributedStringalloc] initWithString:text attributes:attributes];}
效果图:
空白页显示按钮:示例1
- (NSAttributedString*)buttonTitleForEmptyDataSet:(UIScrollView*)scrollView forState:(UIControlState)state {// 设置按钮标题NSString*buttonTitle =@"喜欢我就点点点点我";NSDictionary*attributes = @{NSFontAttributeName:[UIFontboldSystemFontOfSize:17.0f] };return[[NSAttributedStringalloc] initWithString:buttonTitle attributes:attributes];}
效果图:
空白页显示按钮:示例2
按钮点击高亮效果
- (NSAttributedString*)buttonTitleForEmptyDataSet:(UIScrollView*)scrollView forState:(UIControlState)state {NSString*text =@"Learn more";UIFont*font = [UIFontsystemFontOfSize:15.0];// 设置默认状态、点击高亮状态下的按钮字体颜色UIColor*textColor = [UIColorcolorWithHex:(state ==UIControlStateNormal) ?@"007ee5":@"48a1ea"];NSMutableDictionary*attributes = [NSMutableDictionarynew]; [attributes setObject:font forKey:NSFontAttributeName]; [attributes setObject:textColor forKey:NSForegroundColorAttributeName];return[[NSAttributedStringalloc] initWithString:text attributes:attributes];}
效果图:
空白页显示按钮:示例3
按钮标题中点击重试四个字加粗:
#pragma mark - DZNEmptyDataSetSource- (UIImage*)imageForEmptyDataSet:(UIScrollView*)scrollView {return[UIImageimageNamed:@"placeholder_No_Network"];}- (NSAttributedString*)buttonTitleForEmptyDataSet:(UIScrollView*)scrollView forState:(UIControlState)state {NSString*text =@"网络不给力,请点击重试哦~";NSMutableAttributedString*attStr = [[NSMutableAttributedStringalloc] initWithString:text];// 设置所有字体大小为 #15[attStr addAttribute:NSFontAttributeNamevalue:[UIFontsystemFontOfSize:15.0] range:NSMakeRange(0, text.length)];// 设置所有字体颜色为浅灰色[attStr addAttribute:NSForegroundColorAttributeNamevalue:[UIColorlightGrayColor] range:NSMakeRange(0, text.length)];// 设置指定4个字体为蓝色[attStr addAttribute:NSForegroundColorAttributeNamevalue:HexColor(@"#007EE5") range:NSMakeRange(7,4)];returnattStr;}- (CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView*)scrollView {return-70.0f;}#pragma mark - DZNEmptyDataSetDelegate- (void)emptyDataSet:(UIScrollView*)scrollView didTapButton:(UIButton*)button {// button clicked...}- (void)emptyDataSetWillAppear:(UIScrollView*)scrollView {self.tableView.contentOffset =CGPointZero;}
效果图
空白页显示按钮:示例4
// 空白页显示返回按钮图片- (UIImage*)buttonImageForEmptyDataSet:(UIScrollView*)scrollView forState:(UIControlState)state {return[UIImageimageNamed:@"placeholder_return"];}- (void)emptyDataSet:(UIScrollView*)scrollView didTapButton:(UIButton*)button { [self.navigationController popViewControllerAnimated:YES];}
设置按钮的背景颜色
- (nullableUIImage*)buttonBackgroundImageForEmptyDataSet:(UIScrollView*)scrollView forState:(UIControlState)state;
设置按钮图片
- (UIImage*)buttonImageForEmptyDataSet:(UIScrollView*)scrollView forState:(UIControlState)state {return[UIImageimageNamed:@"Image"];}
效果图:
设置图片的 tintColor
- (UIColor*)imageTintColorForEmptyDataSet:(UIScrollView*)scrollView {return[UIColoryellowColor];}
效果图:就是设置图片颜色,脑补中。。。
设置空白页面的背景色
- (UIColor*)backgroundColorForEmptyDataSet:(UIScrollView*)scrollView {UIColor*appleGreenColor = [UIColorcolorWithRed:199/255.0green:237/255.0blue:204/255.0alpha:1.0];returnappleGreenColor;}
效果图:
如果你需要设置更复杂的布局,也可以返回自定义视图:
- (UIView*)customViewForEmptyDataSet:(UIScrollView*)scrollView {UIActivityIndicatorView*activityView = [[UIActivityIndicatorViewalloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; [activityView startAnimating];returnactivityView;}
效果图:
设置图片动画
#pragma mark - DZNEmptyDataSetSource#pragma mark 设置空白页图片- (nullableUIImage*)imageForEmptyDataSet:(UIScrollView*)scrollView {return[UIImageimageNamed:@"lion"];}#pragma mark 设置图片动画: 旋转- (CAAnimation*)imageAnimationForEmptyDataSet:(UIScrollView*)scrollView {CABasicAnimation*animation = [CABasicAnimationanimationWithKeyPath:@"transform"]; animation.fromValue = [NSValuevalueWithCATransform3D:CATransform3DIdentity]; animation.toValue = [NSValuevalueWithCATransform3D:CATransform3DMakeRotation(M_PI_2,0.0,0.0,1.0)]; animation.duration =0.25; animation.cumulative =YES; animation.repeatCount = MAXFLOAT;returnanimation;}#pragma mark - DZNEmptyDataSetDelegate// 向代理请求图像视图动画权限。 默认值为NO。// 确保从 imageAnimationForEmptyDataSet 返回有效的CAAnimation对象:- (BOOL)emptyDataSetShouldAnimateImageView:(UIScrollView*)scrollView {returnYES;}
效果图:
图像视图动画:缩放
#pragma mark - DZNEmptyDataSetSource#pragma mark 设置空白页图片- (nullableUIImage*)imageForEmptyDataSet:(UIScrollView*)scrollView {return[UIImageimageNamed:@"computer"];}#pragma mark 设置图片动画- (CAAnimation*)imageAnimationForEmptyDataSet:(UIScrollView*)scrollView{CABasicAnimation*animation = [CABasicAnimationanimationWithKeyPath:@"bounds"]; animation.duration =1.25; animation.cumulative =NO; animation.repeatCount = MAXFLOAT; animation.toValue = [NSValuevalueWithCGRect:CGRectMake(0,0,45,45)];returnanimation;}#pragma mark - DZNEmptyDataSetDelegate- (BOOL)emptyDataSetShouldAnimateImageView:(UIScrollView*)scrollView {returnYES;}
效果图:
我们发现在官方的ApplicationsDemo 应用中的空白视图中的动画是这样的:
空白视图默认情况下显示一张【静态图片】,当用户点击【静态图片】以后,该图片会被替换成【加载转圈】。
通过阅读源码,可以发现它是这样工作的:
首先在遵循协议的.m文件中声明了一个BOOL类型的变量,用来记录空白页面当前的加载状态:
@property(nonatomic,getter=isLoading)BOOLloading;
然后为该属性设置setter方法,重新加载空数据集视图:
- (void)setLoading:(BOOL)loading{if(self.isLoading == loading) {return; } _loading = loading;// 每次 loading 状态被修改,就刷新空白页面。[self.tableView reloadEmptyDataSet];}
接下来要实现几个关联协议
#pragma mark - DZNEmptyDataSetSource#pragma mark 设置空白页图片- (UIImage*)imageForEmptyDataSet:(UIScrollView*)scrollView {if(self.isLoading) {// 圆形加载图片return[UIImageimageNamed:@"loading_imgBlue_78x78"]; }else{// 默认静态图片return[UIImageimageNamed:@"staticImage"]; }}#pragma mark 图片旋转动画- (CAAnimation*)imageAnimationForEmptyDataSet:(UIScrollView*)scrollView{CABasicAnimation*animation = [CABasicAnimationanimationWithKeyPath:@"transform"]; animation.fromValue = [NSValuevalueWithCATransform3D:CATransform3DIdentity]; animation.toValue = [NSValuevalueWithCATransform3D:CATransform3DMakeRotation(M_PI_2,0.0,0.0,1.0) ]; animation.duration =0.25; animation.cumulative =YES; animation.repeatCount = MAXFLOAT;returnanimation;}#pragma mark - DZNEmptyDataSetDelegate#pragma mark 是否开启动画- (BOOL)emptyDataSetShouldAnimateImageView:(UIScrollView*)scrollView {returnself.isLoading;}#pragma mark 空白页面被点击时刷新页面- (void)emptyDataSet:(UIScrollView*)scrollView didTapView:(UIView*)view {// 空白页面被点击时开启动画,reloadEmptyDataSetself.loading =YES;// 执行加载任务...dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{// 任务加载完成后关闭动画,reloadEmptyDataSetself.loading =NO; });}
此外,你还可以调整内容视图的垂直对齐(垂直偏移量)方式:
// 向上偏移量为表头视图高度/2- (CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView*)scrollView {return-self.tableView.tableHeaderView.frame.size.height/2.0f;}// 或者,返回固定值- (CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView*)scrollView {return-64;}
效果图:
最后,你也可以设置所有组件彼此之间的上下间距(默认间距为11 pt):
- (CGFloat)spaceHeightForEmptyDataSet:(UIScrollView*)scrollView {return25.0f;}
效果图:
实现代理协议
DZNEmptyDataSetDelegate ——实现该协议,可以设置你期望从空白页面返回的的行为,并接收用户交互事件。
设置是否渲染和显示空白页面(默认为YES):
- (BOOL)emptyDataSetShouldDisplay:(UIScrollView*)scrollView {returnYES;}
设置是否以淡入方式显示空白页面。 (默认值为YES)
- (BOOL)emptyDataSetShouldFadeIn:(UIScrollView*)scrollView {returnYES;}
强制显示空数据集:当项目数量大于0时,请求代理是否仍应显示空数据集。(默认值为NO)
- (BOOL)emptyDataSetShouldBeForcedToDisplay:(UIScrollView*)scrollView;
获取交互权限:是否接收用户点击事件(默认为YES):
- (BOOL)emptyDataSetShouldAllowTouch:(UIScrollView*)scrollView {// 如果正在加载中,则不响应用户交互。return!self.isLoading;}
获取滚动权限(默认值为NO):
- (BOOL)emptyDataSetShouldAllowScroll:(UIScrollView*)scrollView;
获取图像动画权限:是否开启图片动画(默认值为NO):
- (BOOL)emptyDataSetShouldAnimateImageView:(UIScrollView*)scrollView {returnYES;}
空白数据集视图被点击时触发该方法:
- (void)emptyDataSet:(UIScrollView*)scrollView didTapView:(UIView*)view {// 处理视图点击事件...}
空白数据集按钮被点击时触发该方法:
- (void)emptyDataSet:(UIScrollView*)scrollView didTapButton:(UIButton*)button {// 处理按钮点击事件...}
空白页将要出现
- (void)emptyDataSetWillAppear:(UIScrollView*)scrollView {// 如果你的空白占位图与需求向左,发生偏移,可如下设置:self.tableView.contentOffset =CGPointZero;}
空白页已经出现
- (void)emptyDataSetDidAppear:(UIScrollView*)scrollView;
空白页将要消失
- (void)emptyDataSetWillDisappear:(UIScrollView*)scrollView;
空白页已经消失
- (void)emptyDataSetDidDisappear:(UIScrollView*)scrollView;
刷新布局
如果你需要刷新空白页面布局,只需调用:
[self.tableView reloadData];
或者
[self.collectionView reloadData];
这取决于你用的是哪一个。
强制布局更新
你还可以调用[self.tableView reloadEmptyDataSet]以使当前空白页面布局无效,并触发布局更新,绕过-reloadData。 如果你的数据源上有很多逻辑处理,当你不需要或者想避免调用-reloadData时这可能很有用。 当使用UIScrollView时,[self.scrollView reloadEmptyDataSet]是刷新内容的唯一方法。
二、用 DZNEmptyDataSet 实现占位图效果
不得不说,其使用方法极其简单,用 cocoapods 导入可以,直接拖进项目也行。。。接着~~~
导入头文件
#import"UIScrollView+EmptyDataSet.h"
添加代理协议
@interfaceNNViewController()
设置代理协议
_tableView = [[UITableViewalloc] initWithFrame:CGRectMake(0,0, NNSCREENWIDTH, NNSCREENHEIGHT) style:UITableViewStylePlain]; [self.view addSubview:self.tableView];self.tableView.dataSource =self;self.tableView.emptyDataSetSource =self;self.tableView.emptyDataSetDelegate =self;self.tableView.tableFooterView = [[UIViewalloc] init];
设置空白页展示图片
#pragma mark - DZNEmptyDataSetSource// 返回图片- (UIImage*)imageForEmptyDataSet:(UIScrollView*)scrollView{return[UIImageimageNamed:@"Screenshot_Slack"];}
到这里简单的占位图已经做好了,是不是觉得 so 轻松 so easy!下面是效果图:
效果图
三、接下来是 DZNEmptyDataSet 的其它使用方法
DZNEmptyDataSet 框架扩展性极强,可以在占位图上添加图片、标题文字、详情文字等等一堆东西,总有一款适合你!下边是一些具体用法及效果图。
3.1 添加标题文字
// 返回标题文字- (NSAttributedString*)titleForEmptyDataSet:(UIScrollView*)scrollView {NSString*text =@"这是一张空白页";NSDictionary*attribute = @{NSFontAttributeName: [UIFontboldSystemFontOfSize:18.0f],NSForegroundColorAttributeName: [UIColordarkGrayColor]};return[[NSAttributedStringalloc] initWithString:text attributes:attribute];}
返回标题文字
3.2 添加详情文字
// 返回详情文字- (NSAttributedString*)descriptionForEmptyDataSet:(UIScrollView*)scrollView {NSString*text =@"哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈";NSMutableParagraphStyle*paragraph = [NSMutableParagraphStylenew]; paragraph.lineBreakMode =NSLineBreakByWordWrapping; paragraph.alignment =NSTextAlignmentCenter;NSDictionary*attribute = @{NSFontAttributeName: [UIFontsystemFontOfSize:14.0f],NSForegroundColorAttributeName: [UIColorlightGrayColor],NSParagraphStyleAttributeName: paragraph};return[[NSAttributedStringalloc] initWithString:text attributes:attribute];}
详情文字
3.3 添加可以点击的按钮 上面带文字
// 返回可以点击的按钮 上面带文字- (NSAttributedString*)buttonTitleForEmptyDataSet:(UIScrollView*)scrollView forState:(UIControlState)state {NSDictionary*attribute = @{NSFontAttributeName: [UIFontboldSystemFontOfSize:17.0f]};return[[NSAttributedStringalloc] initWithString:@"哈喽"attributes:attribute];}//#pragma mark - DZNEmptyDataSetDelegate// 处理按钮的点击事件- (void)emptyDataSet:(UIScrollView*)scrollView didTapButton:(UIButton*)button {NSURL*url = [NSURLURLWithString:[NSStringstringWithFormat:@"https://www.baidu.com"]];if([[UIApplicationsharedApplication] canOpenURL:url]) { [[UIApplicationsharedApplication] openURL:url]; }}
可以点击的按钮
3.4 空白区域点击事件
// 空白区域点击事件- (void)emptyDataSet:(UIScrollView*)scrollView didTapView:(UIView*)view {UIAlertView*alert = [[UIAlertViewalloc] initWithTitle:@"哈哈"message:@"最近咋样"delegate:nilcancelButtonTitle:@"别点我"otherButtonTitles:@"点我干啥",nil]; [alert show];}
空白区域点击事件
3.5 改变标题文字与详情文字的距离
// 标题文字与详情文字的距离- (CGFloat)spaceHeightForEmptyDataSet:(UIScrollView*)scrollView {return100;}
标题文字与详情文字的距离
3.6 空白区域的颜色自定义
// 返回空白区域的颜色自定义- (UIColor*)backgroundColorForEmptyDataSet:(UIScrollView*)scrollView {return[UIColorcyanColor];}
空白区域的颜色自定义
3.7 标题文字与详情文字同时调整垂直偏移量
// 标题文字与详情文字同时调整垂直偏移量- (CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView*)scrollView {return-100;}
标题文字与详情文字同时调整垂直偏移量
3.8 添加动画效果
#pragma mark - DZNEmptyDataSetSource// 返回图片- (UIImage*)imageForEmptyDataSet:(UIScrollView*)scrollView{return[UIImageimageNamed:@"icon_wwdc"];}- (CAAnimation*)imageAnimationForEmptyDataSet:(UIScrollView*)scrollView {CABasicAnimation*animation = [CABasicAnimationanimationWithKeyPath:@"transform"]; animation.fromValue = [NSValuevalueWithCATransform3D:CATransform3DIdentity]; animation.toValue = [NSValuevalueWithCATransform3D:CATransform3DMakeRotation(M_PI_2,0.0,0.0,1.0)]; animation.duration =0.25; animation.cumulative =YES; animation.repeatCount = MAXFLOAT;returnanimation;}#pragma mark - DZNEmptyDataSetDelegate// 图片是否要动画效果,默认NO- (BOOL)emptyDataSetShouldAnimateImageView:(UIScrollView*)scrollView {returnYES;}
动画效果
3.9 其它方法
#pragma mark - DZNEmptyDataSetSource// 返回图片的 tintColor- (UIColor*)imageTintColorForEmptyDataSet:(UIScrollView*)scrollView {return[UIColoryellowColor];}// 返回可点击按钮的 image- (UIImage*)buttonImageForEmptyDataSet:(UIScrollView*)scrollView forState:(UIControlState)state {return[UIImageimageNamed:@"icon_wwdc"];}// 返回可点击按钮的 backgroundImage- (UIImage*)buttonBackgroundImageForEmptyDataSet:(UIScrollView*)scrollView forState:(UIControlState)state {return[UIImageimageNamed:@"icon_wwdc"];}// 返回自定义 view- (UIView*)customViewForEmptyDataSet:(UIScrollView*)scrollView {returnnil;}#pragma mark - DZNEmptyDataSetDelegate// 是否显示空白页,默认YES- (BOOL)emptyDataSetShouldDisplay:(UIScrollView*)scrollView {returnYES;}// 是否允许点击,默认YES- (BOOL)emptyDataSetShouldAllowTouch:(UIScrollView*)scrollView {returnYES;}// 是否允许滚动,默认NO- (BOOL)emptyDataSetShouldAllowScroll:(UIScrollView*)scrollView {returnYES;}// 图片是否要动画效果,默认NO- (BOOL)emptyDataSetShouldAnimateImageView:(UIScrollView*)scrollView {returnYES;}// 空白页将要出现- (void)emptyDataSetWillAppear:(UIScrollView*)scrollView { }// 空白页已经出现- (void)emptyDataSetDidAppear:(UIScrollView*)scrollView { }// 空白页将要消失- (void)emptyDataSetWillDisappear:(UIScrollView*)scrollView { }// 空白页已经消失- (void)emptyDataSetDidDisappear:(UIScrollView*)scrollView { }
------------------华丽的分割线------------------
小结:本小白只在 UITableView 上试用了 DZNEmptyDataSet 框架。。。公司要求年前再迭代一个版本,时间不太充裕因此没逐一去试。。。哪位大神如果试了 UICollectionView 或 UIScrollView 发现有问题,还请不吝赐教!