DZNEmptyDataSet——空白數(shù)據(jù)集顯示框架

注:以下內(nèi)容來(lái)源于官方源碼、 README 文檔、Demo 應(yīng)用以及個(gè)人使用總結(jié) !

DZNEmptyDataSet

DZNEmptyDataSet 是基于 UITableView/UICollectionView 的擴(kuò)展(category)類(lèi),用于在空白頁(yè)面顯示提示信息。

這是 iOS 內(nèi)建的標(biāo)準(zhǔn),用于處理空白列表和空白集合視圖。默認(rèn)情況下,如果你的列表視圖是空白的,屏幕上什么也不會(huì)顯示,它給用戶(hù)的體驗(yàn)不是很好。

集成 DZNEmptyDataSet 之后,你只需要實(shí)現(xiàn)相關(guān)協(xié)議,就可以很好地處理空白視圖,合理美觀(guān)地顯示出提示信息。

其他效果圖參考

使用該框架的項(xiàng)目

將你的項(xiàng)目添加到列表中 并且提供一張(320px)的效果圖。

空數(shù)據(jù)設(shè)計(jì)模式(The Empty Data Set Pattern)

空數(shù)據(jù)設(shè)計(jì)模式(The Empty Data Set Pattern)也被稱(chēng)為 Empty state 或者 Blank Slate。

大多數(shù)應(yīng)用程序會(huì)顯示列表視圖(UITableView)、集合視圖(UICollectionView),但有些時(shí)候這些頁(yè)面可能是空白的,特別是對(duì)于那些剛創(chuàng)建空賬戶(hù)的新用戶(hù)來(lái)說(shuō)。 空白界面會(huì)對(duì)用戶(hù)造成不知道如何進(jìn)行下一步操作的困惑,因?yàn)橛脩?hù)不知道屏幕空白的原因是錯(cuò)誤、Bug、網(wǎng)絡(luò)異常,還是用戶(hù)應(yīng)該自己新建內(nèi)容以恢復(fù)APP的正常狀態(tài)。

請(qǐng)閱讀這篇非常有趣的文章 Designing For The Empty States。

iOS 9人機(jī)界面指南 1.4.2 中也提及過(guò)類(lèi)似的指引:

如果應(yīng)用中所有的功能當(dāng)前都不可用,那么應(yīng)該顯示一些內(nèi)容來(lái)解釋當(dāng)前的情形,并建議用戶(hù)如何進(jìn)行后續(xù)操作。這部分內(nèi)容給予了用戶(hù)以反饋,使用戶(hù)相信你的應(yīng)用現(xiàn)在沒(méi)問(wèn)題。同時(shí)這也可以穩(wěn)定用戶(hù)情緒,讓他們決定是否要采取糾正措施,繼續(xù)使用應(yīng)用,還是切換到另一個(gè)應(yīng)用。

Empty Data Sets 有助于:

  • 避免使用空白屏幕,并向用戶(hù)傳達(dá)屏幕空白的原因。
  • 用戶(hù)指引(特別是作為引導(dǎo)頁(yè)面)。
  • 避免其他中斷機(jī)制,如顯示錯(cuò)誤警報(bào)。
  • 一致性和改善用戶(hù)體驗(yàn)。
  • 傳遞品牌價(jià)值。

特性

  • 兼容 UITableViewUICollectionView 。 也兼容 UISearchDisplayControllerUIScrollView 。
  • 通過(guò)顯示圖片、標(biāo)題、詳細(xì)文本、按鈕,提供布局外觀(guān)的多種可能性。
  • 使用 NSAttributedString 類(lèi)提供更容易定制的外觀(guān)。
  • 使用 Auto Layout 以自動(dòng)將內(nèi)容集中到表格視圖,并支持自動(dòng)旋轉(zhuǎn)。 也接受自定義垂直和水平對(duì)齊。
  • 自定義背景顏色。
  • 允許在整個(gè)表格矩形上輕敲手勢(shì)(有助于放棄第一個(gè)響應(yīng)者或類(lèi)似操作)。
  • 提供更高級(jí)的定制,允許自定義視圖。
  • 兼容 Storyboard。
  • 兼容iOS 6,tvOS 9或更高版本。
  • 兼容iPhone,iPad和Apple TV。
  • 支持 App Store 。

這個(gè)庫(kù)已經(jīng)被設(shè)計(jì)為不需要通過(guò)擴(kuò)展(extend) UITableViewUICollectionView 類(lèi)的方式來(lái)實(shí)現(xiàn)了。 使用 UITableViewControllerUICollectionViewController 類(lèi)仍然可以奏效。 只要通過(guò)遵循 DZNEmptyDataSetSourceDZNEmptyDataSetDelegate 協(xié)議,您將能夠完全自定義應(yīng)用程序的空狀態(tài)的內(nèi)容和外觀(guān)。

安裝

支持 CocoaPods 導(dǎo)入

pod 'DZNEmptyDataSet'

使用

完整文檔參考:CocoaPods 自動(dòng)生成文檔

導(dǎo)入

手動(dòng)導(dǎo)入:

#import "UIScrollView+EmptyDataSet.h"

作為框架導(dǎo)入:

#import <DZNEmptyDataSet/UIScrollView+EmptyDataSet.h>

遵循協(xié)議

在需要顯示空白數(shù)據(jù)集的視圖控制器中,聲明該視圖控制器遵守DZNEmptyDataSetSource、DZNEmptyDataSetDelegate 協(xié)議:

// 遵守 DZNEmptyDataSetSource 、DZNEmptyDataSetDelegate 協(xié)議
@interface MainViewController : UITableViewController <DZNEmptyDataSetSource, DZNEmptyDataSetDelegate>

- (void)viewDidLoad {
    [super viewDidLoad];

    self.tableView.emptyDataSetSource = self;
    self.tableView.emptyDataSetDelegate = self;
}

實(shí)現(xiàn)數(shù)據(jù)源協(xié)議 DZNEmptyDataSetSource

實(shí)現(xiàn)該協(xié)議可以設(shè)置你想要在空白頁(yè)面顯示的內(nèi)容,并且充分利用 NSAttributedString 類(lèi)來(lái)自定義文本樣式。

空白頁(yè)顯示圖片

// MARK: 空白頁(yè)顯示圖片
- (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView {
    return [UIImage imageNamed:@"lion"];
}

實(shí)現(xiàn) <DZNEmptyDataSetSource> 協(xié)議的 imageForEmptyDataSet: 方法,返回一張圖片即可。

頁(yè)面顯示效果如下圖所示:

空白頁(yè)顯示圖片和標(biāo)題

繼續(xù)上一個(gè)示例,在圖片下方添加標(biāo)題文字:

// MARK: 空白頁(yè)顯示圖片
- (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView {
    return [UIImage imageNamed:@"lion"];
}

// MARK: 空白頁(yè)顯示標(biāo)題
- (NSAttributedString *)titleForEmptyDataSet:(UIScrollView *)scrollView {
    NSString *title = @"獅子王";
    NSDictionary *attributes = @{
        NSFontAttributeName:[UIFont boldSystemFontOfSize:18.0f],
        NSForegroundColorAttributeName:[UIColor darkGrayColor]
    };
    return [[NSAttributedString alloc] initWithString:title attributes:attributes];
}

頁(yè)面顯示效果如下圖所示:

空白頁(yè)顯示圖片、標(biāo)題、詳細(xì)描述

這一次,除了在空白頁(yè)面顯示一張圖片和標(biāo)題,我們還可以在下方添加更詳細(xì)的描述文字:

// MARK: 空白頁(yè)顯示圖片
- (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView {
    return [UIImage imageNamed:@"lion"];
}

// MARK: 空白頁(yè)顯示標(biāo)題
- (NSAttributedString *)titleForEmptyDataSet:(UIScrollView *)scrollView {
    NSString *title = @"獅子王";
    NSDictionary *attributes = @{
        NSFontAttributeName:[UIFont boldSystemFontOfSize:18.0f],
        NSForegroundColorAttributeName:[UIColor darkGrayColor]
    };
    return [[NSAttributedString alloc] initWithString:title attributes:attributes];
}

// MARK: 空白頁(yè)顯示詳細(xì)描述
- (NSAttributedString *)descriptionForEmptyDataSet:(UIScrollView *)scrollView {
    NSString *text = @"我只在必要時(shí)才勇敢,勇敢并不意味著要到處闖禍!";
  
    NSMutableParagraphStyle *paragraph = [NSMutableParagraphStyle new];
    paragraph.lineBreakMode = NSLineBreakByWordWrapping;
    paragraph.alignment = NSTextAlignmentCenter;
    
    NSDictionary *attributes = @{
        NSFontAttributeName:[UIFont systemFontOfSize:14.0f],
        NSForegroundColorAttributeName:[UIColor lightGrayColor],
        NSParagraphStyleAttributeName:paragraph
    };
    
    return [[NSAttributedString alloc] initWithString:text attributes:attributes];
}

頁(yè)面顯示效果如下圖所示:

設(shè)置內(nèi)容的垂直偏移量

你還可以調(diào)整內(nèi)容視圖的垂直對(duì)齊(垂直偏移量)方式:

// 向上偏移量為表頭視圖高度/2
- (CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView *)scrollView {
    return -self.tableView.tableHeaderView.frame.size.height/2.0f;
}

或者返回一個(gè)固定值:

- (CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView *)scrollView {
    return -64;
}

頁(yè)面效果如下所示:

設(shè)置內(nèi)容元素之間的垂直間距

你也可以設(shè)置所有組件彼此之間的上下間距(默認(rèn)間距為11 pt):

- (CGFloat)spaceHeightForEmptyDataSet:(UIScrollView *)scrollView {
    return 50.0f;
}

純粹為了測(cè)試,我把這個(gè)垂直間距的值設(shè)置的很大,頁(yè)面效果如下所示:

空白頁(yè)添加按鈕

除了在空白頁(yè)添加靜態(tài)的圖片和文本,我們還可以在空白頁(yè)添加可交互的按鈕。設(shè)置空白頁(yè)按鈕的文字:

#pragma mark - <DZNEmptyDataSetSource>

// MARK: 空白頁(yè)顯示圖片
- (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView {
    return [UIImage imageNamed:@"unlogin"];
}

// MARK: 空白頁(yè)顯示標(biāo)題
- (NSAttributedString *)titleForEmptyDataSet:(UIScrollView *)scrollView {
    NSString *title = @"您當(dāng)前未登錄";
    NSDictionary *attributes = @{
        NSFontAttributeName:[UIFont boldSystemFontOfSize:14.0f],
        NSForegroundColorAttributeName:[UIColor lightGrayColor]
    };
    return [[NSAttributedString alloc] initWithString:title attributes:attributes];
}

// MARK: 空白頁(yè)添加按鈕,設(shè)置按鈕文字
- (NSAttributedString *)buttonTitleForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state {
    // 設(shè)置按鈕標(biāo)題
    NSString *buttonTitle = @"點(diǎn)擊登錄";
    NSDictionary *attributes = @{
        NSFontAttributeName:[UIFont boldSystemFontOfSize:17.0f],
        NSForegroundColorAttributeName: [UIColor flatSkyBlueColor]
    };
    return [[NSAttributedString alloc] initWithString:buttonTitle attributes:attributes];
}

#pragma mark - <DZNEmptyDataSetDelegate>

- (void)emptyDataSet:(UIScrollView *)scrollView didTapButton:(UIButton *)button {
    // 處理空白頁(yè)面按鈕點(diǎn)擊事件
    NSLog(@"處理空白頁(yè)面按鈕點(diǎn)擊事件");
    
}

正如以上示例代碼所示,
實(shí)現(xiàn) <DZNEmptyDataSetSource> 協(xié)議的 buttonTitleForEmptyDataSet:forState: 方法,并返回一個(gè) NSAttributedString 實(shí)例來(lái)設(shè)置按鈕標(biāo)題。
實(shí)現(xiàn) <DZNEmptyDataSetDelegate> 協(xié)議的 emptyDataSet:didTapButton: 方法,可以處理該按鈕的點(diǎn)擊響應(yīng)事件。

頁(yè)面效果如下所示:

注意到,設(shè)置按鈕標(biāo)題的 buttonTitleForEmptyDataSet:forState: 方法中,還返回了 UIControlState 屬性,因此我們還可以設(shè)置按鈕高亮或者默認(rèn)狀態(tài)下的顏色,示例代碼如下:

- (NSAttributedString *)buttonTitleForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state {
    // 設(shè)置按鈕標(biāo)題
    NSString *buttonTitle = @"按鈕標(biāo)題";
    UIColor *buttonColor = [UIColor colorWithHexString:(state == UIControlStateNormal) ? @"007ee5" : @"48a1ea"];
    NSDictionary *attributes = @{
        NSFontAttributeName:[UIFont systemFontOfSize:15.0],
        NSForegroundColorAttributeName: buttonColor
    };
    return [[NSAttributedString alloc] initWithString:buttonTitle attributes:attributes];
}

說(shuō)明:

  • colorWithHexString: 表示通過(guò)十六進(jìn)制值設(shè)置顏色,該方法來(lái)自 Chameleon 顏色框架。
  • statement ? True : False 是一個(gè)三元表達(dá)式,判斷值為真,則執(zhí)行 True,否則執(zhí)行 False。

空白頁(yè)添加按鈕,個(gè)性化按鈕標(biāo)題

空白頁(yè)面中僅添加一張圖片和一個(gè)按鈕標(biāo)題,把按鈕標(biāo)題中 「點(diǎn)擊重試」四個(gè)字顏色高亮加粗:

#pragma mark - <DZNEmptyDataSetSource>

// MARK: 空白頁(yè)顯示圖片
- (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView {
    return [UIImage imageNamed:@"placeholder_no_network"];
}

// MARK: 空白頁(yè)添加按鈕,設(shè)置按鈕文字
- (NSAttributedString *)buttonTitleForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state {
    // 設(shè)置按鈕標(biāo)題
    NSString *buttonTitle = @"網(wǎng)絡(luò)不給力,請(qǐng)點(diǎn)擊重試哦~";

    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:buttonTitle];
    // 設(shè)置所有字體大小為 #15
    [attributedString addAttribute:NSFontAttributeName
                             value:[UIFont systemFontOfSize:15.0]
                             range:NSMakeRange(0, buttonTitle.length)];
    // 設(shè)置所有字體顏色為淺灰色
    [attributedString addAttribute:NSForegroundColorAttributeName
                             value:[UIColor lightGrayColor]
                             range:NSMakeRange(0, buttonTitle.length)];
    // 設(shè)置指定4個(gè)字體為藍(lán)色
    [attributedString addAttribute:NSForegroundColorAttributeName
                             value:HexColor(@"#007EE5")
                             range:NSMakeRange(7, 4)];
    return attributedString;
    
}

// MARK: 空白頁(yè)背景顏色
- (nullable UIColor *)backgroundColorForEmptyDataSet:(UIScrollView *)scrollView {
    return [UIColor colorWithHexString:@"#f0f3f5"];
}

// MARK: 設(shè)置空白頁(yè)內(nèi)容的垂直便宜量
- (CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView *)scrollView {
    return -70.0f;
}

#pragma mark - <DZNEmptyDataSetDelegate>

- (void)emptyDataSet:(UIScrollView *)scrollView didTapButton:(UIButton *)button {
    // 處理空白頁(yè)面按鈕點(diǎn)擊事件
    NSLog(@"處理空白頁(yè)面按鈕點(diǎn)擊事件");
}

- (void)emptyDataSetWillAppear:(UIScrollView *)scrollView {
    self.tableView.contentOffset = CGPointZero;
}

頁(yè)面效果如下所示:

空白頁(yè)添加按鈕,設(shè)置按鈕背景圖片

通過(guò)實(shí)現(xiàn) <DZNEmptyDataSetSource> 協(xié)議的 buttonBackgroundImageForEmptyDataSet: forState: 方法,你可以設(shè)置默認(rèn)狀態(tài)(即 UIControlStateNormal)和高亮狀態(tài)(即 UIControlStateHighlighted)下的按鈕背景圖片。

背景圖片類(lèi)似這樣的:

示例代碼如下:

#pragma mark - <DZNEmptyDataSetSource>

// MARK: 空白頁(yè)顯示圖片
- (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView {
    if (self.isLoading) {
        return [UIImage imageNamed:@"loading_imgBlue_78x78"];
    } else {
        return [UIImage imageNamed:@"placeholder_foursquare"];
    }
}

// MARK: 空白頁(yè)圖片動(dòng)畫(huà)
- (CAAnimation *)imageAnimationForEmptyDataSet:(UIScrollView *)scrollView {
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
    animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
    animation.toValue = [NSValue valueWithCATransform3D: CATransform3DMakeRotation(M_PI_2, 0.0, 0.0, 1.0) ];
    animation.duration = 0.25;
    animation.cumulative = YES;
    animation.repeatCount = MAXFLOAT;
    
    return animation;
}

// MARK: 空白頁(yè)顯示詳細(xì)描述
- (NSAttributedString *)descriptionForEmptyDataSet:(UIScrollView *)scrollView {
    NSString *text = @"Nobody has liked or commented on your check-ins yet.";
  
    NSMutableParagraphStyle *paragraph = [NSMutableParagraphStyle new];
    paragraph.lineBreakMode = NSLineBreakByWordWrapping;
    paragraph.alignment = NSTextAlignmentCenter;
    
    NSDictionary *attributes = @{
        NSFontAttributeName:[UIFont systemFontOfSize:14.0f],
        NSForegroundColorAttributeName:[UIColor colorWithHexString:@"#cecbc6"],
        NSParagraphStyleAttributeName:paragraph
    };
    
    return [[NSAttributedString alloc] initWithString:text attributes:attributes];
}

// MARK: 空白頁(yè)添加按鈕,設(shè)置按鈕文字
- (NSAttributedString *)buttonTitleForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state {
    // 設(shè)置按鈕標(biāo)題
    NSString *buttonTitle = @"Add friends to get started!";
    UIColor *buttonColor = [UIColor colorWithHexString:(state == UIControlStateNormal) ? @"00aeef" : @"ffffff"];
    NSDictionary *attributes = @{
        NSFontAttributeName:[UIFont systemFontOfSize:14.0],
        NSForegroundColorAttributeName: buttonColor
    };
    return [[NSAttributedString alloc] initWithString:buttonTitle attributes:attributes];
}

// MARK: 空白頁(yè)添加按鈕,設(shè)置按鈕背景圖片
- (UIImage *)buttonBackgroundImageForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state {
   
    UIImage *image;
    if (state == UIControlStateNormal) {
        image = [UIImage imageNamed:@"button_background_foursquare_normal"];
    }
    if (state == UIControlStateHighlighted) {
        image = [UIImage imageNamed:@"button_background_foursquare_highlight"];
    }
    
    UIEdgeInsets capInsets = UIEdgeInsetsMake(25.0, 25.0, 25.0, 25.0);
    UIEdgeInsets rectInsets = UIEdgeInsetsMake(0.0, 10, 0.0, 10);

    return [[image resizableImageWithCapInsets:capInsets resizingMode:UIImageResizingModeStretch] imageWithAlignmentRectInsets:rectInsets];
}

// MARK: 空白頁(yè)背景顏色
- (nullable UIColor *)backgroundColorForEmptyDataSet:(UIScrollView *)scrollView {
    return [UIColor colorWithHexString:@"#fcfcfa"];
}

// MARK: 設(shè)置各個(gè)視圖元素之間的垂直間距,默認(rèn)為 11pt
- (CGFloat)spaceHeightForEmptyDataSet:(UIScrollView *)scrollView {
    return 9.0;
}

#pragma mark - <DZNEmptyDataSetDelegate>

- (void)emptyDataSet:(UIScrollView *)scrollView didTapButton:(UIButton *)button {
    // 處理空白頁(yè)面按鈕點(diǎn)擊事件
    NSLog(@"處理空白頁(yè)面按鈕點(diǎn)擊事件");
    
    self.loading = YES;
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.loading = NO;
    });
    
}

- (BOOL)emptyDataSetShouldAnimateImageView:(UIScrollView *)scrollView {
    return self.isLoading;
}

以上代碼設(shè)置了按鈕文字標(biāo)題,和按鈕的背景圖片。頁(yè)面效果如下所示:

設(shè)置按鈕的背景顏色

通過(guò)設(shè)置按鈕背景圖片的方法也可以設(shè)置按鈕背景顏色:

- (UIImage *)buttonBackgroundImageForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state {
    
    UIImage *image = [UIImage imageWithColor:[UIColor greenColor]];
    return image;
}

以上代碼使用了 YYKit 框架中的 imageWithColor: 方法,可以通過(guò)顏色生成純色圖片。

設(shè)置按鈕圖片

如果你的按鈕定制化需求很高,也通過(guò)實(shí)現(xiàn) buttonImageForEmptyDataSet: forState: 方法,你可以直接把按鈕設(shè)置成一張圖片:

#pragma mark - <DZNEmptyDataSetSource>

// MARK: 空白頁(yè)顯示圖片
- (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView {
    return [UIImage imageNamed:@"placeholder_network_error_404"];
}

// MARK: 空白頁(yè)顯示標(biāo)題
- (NSAttributedString *)titleForEmptyDataSet:(UIScrollView *)scrollView {
    NSString *title = @"您當(dāng)前未連接網(wǎng)絡(luò)";
    NSDictionary *attributes = @{
        NSFontAttributeName:[UIFont systemFontOfSize:15.0],
        NSForegroundColorAttributeName:[UIColor colorWithHexString:@"#25282b"]
    };
    return [[NSAttributedString alloc] initWithString:title attributes:attributes];
}

// MARK: 空白頁(yè)添加按鈕,設(shè)置按鈕圖片
- (UIImage *)buttonImageForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state {
    return [UIImage imageNamed:@"button_Image"];
}


#pragma mark - <DZNEmptyDataSetDelegate>

- (void)emptyDataSet:(UIScrollView *)scrollView didTapButton:(UIButton *)button {
    // 處理空白頁(yè)面按鈕點(diǎn)擊事件
    NSLog(@"處理空白頁(yè)面按鈕點(diǎn)擊事件");
    
}

頁(yè)面效果如下所示:

設(shè)置圖片的 tintColor 屬性

就是設(shè)置圖片顏色,腦補(bǔ)中。。。

- (UIColor *)imageTintColorForEmptyDataSet:(UIScrollView *)scrollView {
    return [UIColor yellowColor];
}

設(shè)置空白頁(yè)面的背景色

通過(guò)實(shí)現(xiàn) backgroundColorForEmptyDataSet: 方法并返回 UIColor 實(shí)例就可以設(shè)置空白頁(yè)面的背景色了,這個(gè)方法,之前的示例代碼中也出現(xiàn)過(guò)。

我們?cè)谏弦粋€(gè)示例代碼中添加以下代碼來(lái)對(duì)比看看:

// MARK: 空白頁(yè)背景顏色
- (nullable UIColor *)backgroundColorForEmptyDataSet:(UIScrollView *)scrollView {
    return [UIColor colorWithHexString:@"#f0f3f5"];
}

頁(yè)面效果如下所示:

設(shè)置自定義視圖

如果你需要設(shè)置更復(fù)雜的布局,也可以設(shè)置并返回自定義視圖:

- (UIView *)customViewForEmptyDataSet:(UIScrollView *)scrollView {
    UIActivityIndicatorView *activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    [activityView startAnimating];
    return activityView;
}

以上代碼在空白頁(yè)面中顯示一個(gè)加載視圖,頁(yè)面效果如下所示:

設(shè)置圖片動(dòng)畫(huà)

為圖片添加旋轉(zhuǎn)動(dòng)畫(huà)。

#pragma mark - <DZNEmptyDataSetSource>

// MARK: 空白頁(yè)顯示圖片
- (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView {
    return [UIImage imageNamed:@"lion"];
}

// MARK: 設(shè)置圖片動(dòng)畫(huà): 旋轉(zhuǎn)
- (CAAnimation *)imageAnimationForEmptyDataSet:(UIScrollView *)scrollView {
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath: @"transform"];
    
    animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
    animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI_2, 0.0, 0.0, 1.0)];
    
    animation.duration = 0.25;
    animation.cumulative = YES;
    animation.repeatCount = MAXFLOAT;
    
    return animation;
}

#pragma mark - DZNEmptyDataSetDelegate

// 向代理請(qǐng)求圖像視圖動(dòng)畫(huà)權(quán)限。 默認(rèn)值為NO。
// 確保從 imageAnimationForEmptyDataSet 返回有效的 CAAnimation 對(duì)象:
- (BOOL)emptyDataSetShouldAnimateImageView:(UIScrollView *)scrollView {
    return YES;
}

頁(yè)面效果如下所示:

設(shè)置圖片動(dòng)畫(huà),縮放動(dòng)畫(huà)

#pragma mark - <DZNEmptyDataSetSource>

// MARK: 空白頁(yè)顯示圖片
- (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView {
    return [UIImage imageNamed:@"placeholder_heart"];
}

// MARK: 設(shè)置圖片動(dòng)畫(huà): 縮放動(dòng)畫(huà)
- (CAAnimation *)imageAnimationForEmptyDataSet:(UIScrollView *)scrollView
{
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"];
    animation.duration = 1.25;
    animation.cumulative = NO;
    animation.repeatCount = MAXFLOAT;
    animation.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 45, 45)];

    return animation;
}

#pragma mark - DZNEmptyDataSetDelegate

// 向代理請(qǐng)求圖像視圖動(dòng)畫(huà)權(quán)限。 默認(rèn)值為NO。
// 確保從 imageAnimationForEmptyDataSet 返回有效的 CAAnimation 對(duì)象:
- (BOOL)emptyDataSetShouldAnimateImageView:(UIScrollView *)scrollView {
    return YES;
}

頁(yè)面效果如下所示:

我們發(fā)現(xiàn)在官方的 Applications Demo 應(yīng)用中的空白視圖中的動(dòng)畫(huà)是這樣的:

空白視圖默認(rèn)情況下顯示一張【靜態(tài)圖片】,當(dāng)用戶(hù)點(diǎn)擊【靜態(tài)圖片】以后,該圖片會(huì)被替換成【加載轉(zhuǎn)圈】。

通過(guò)閱讀源碼,可以發(fā)現(xiàn)它是這樣工作的:

  1. 首先在遵循協(xié)議的視圖控制器實(shí)現(xiàn)文件中聲明了一個(gè) BOOL 類(lèi)型的變量,用來(lái)記錄空白頁(yè)面當(dāng)前的加載狀態(tài):
@property (nonatomic, assign, getter=isLoading) BOOL loading;
  1. 然后為該屬性設(shè)置 setter 方法,重新加載空數(shù)據(jù)集視圖:
// 設(shè)置當(dāng)前頁(yè)面的加載狀態(tài)
- (void)setLoading:(BOOL)loading {
    
    // 如果當(dāng)前頁(yè)面的加載狀態(tài)和之前一樣,返回
    if (self.isLoading == loading) {
        return;
    }
    
    // 不一樣,則刷新空白頁(yè)面
    _loading = loading;
    [self.tableView reloadEmptyDataSet];
}
  1. 接下來(lái)要實(shí)現(xiàn)幾個(gè)關(guān)聯(lián)協(xié)議
#pragma mark - DZNEmptyDataSetSource
#pragma mark 設(shè)置空白頁(yè)圖片
- (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView {
    if (self.isLoading) {
        // 圓形加載圖片
        return [UIImage imageNamed:@"loading_imgBlue_78x78"];
    }else {
        // 默認(rèn)靜態(tài)圖片
        return [UIImage imageNamed:@"staticImage"];
    }
}

#pragma mark 圖片旋轉(zhuǎn)動(dòng)畫(huà)
- (CAAnimation *)imageAnimationForEmptyDataSet:(UIScrollView *)scrollView
{
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
    animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
    animation.toValue = [NSValue valueWithCATransform3D: CATransform3DMakeRotation(M_PI_2, 0.0, 0.0, 1.0) ];
    animation.duration = 0.25;
    animation.cumulative = YES;
    animation.repeatCount = MAXFLOAT;
    
    return animation;
}

#pragma mark - DZNEmptyDataSetDelegate
#pragma mark 是否開(kāi)啟動(dòng)畫(huà)
- (BOOL)emptyDataSetShouldAnimateImageView:(UIScrollView *)scrollView {
    return self.isLoading;
}

#pragma mark 空白頁(yè)面被點(diǎn)擊時(shí)刷新頁(yè)面
- (void)emptyDataSet:(UIScrollView *)scrollView didTapView:(UIView *)view {
    // 空白頁(yè)面被點(diǎn)擊時(shí)開(kāi)啟動(dòng)畫(huà),reloadEmptyDataSet
    self.loading = YES;
    // 執(zhí)行加載任務(wù)...
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 任務(wù)加載完成后關(guān)閉動(dòng)畫(huà),reloadEmptyDataSet
        self.loading = NO;
    });
}

實(shí)現(xiàn)代理協(xié)議 DZNEmptyDataSetDelegate

通過(guò)實(shí)現(xiàn) DZNEmptyDataSetDelegate 協(xié)議,可以設(shè)置你期望從空白頁(yè)面返回的的行為,并接收用戶(hù)交互事件。

  • 設(shè)置是否 渲染和顯示空白頁(yè)面 (默認(rèn)值為 YES)
- (BOOL)emptyDataSetShouldDisplay:(UIScrollView *)scrollView {
    return YES;
}
  • 設(shè)置是否 以淡入方式顯示空白頁(yè)面 。 (默認(rèn)值為 YES)
- (BOOL)emptyDataSetShouldFadeIn:(UIScrollView *)scrollView { 
    return YES;
}
  • 強(qiáng)制顯示空數(shù)據(jù)集:當(dāng)項(xiàng)目數(shù)量大于0時(shí),請(qǐng)求代理是否仍應(yīng)顯示空數(shù)據(jù)集。(默認(rèn)值為NO
- (BOOL)emptyDataSetShouldBeForcedToDisplay:(UIScrollView *)scrollView;
  • 獲取交互權(quán)限:是否接收用戶(hù)點(diǎn)擊事件 (默認(rèn)值為 YES):
- (BOOL)emptyDataSetShouldAllowTouch:(UIScrollView *)scrollView {
    // 如果正在加載中,則不響應(yīng)用戶(hù)交互。
    return !self.isLoading;
}
  • 獲取滾動(dòng)權(quán)限(默認(rèn)值為 NO):
- (BOOL)emptyDataSetShouldAllowScroll:(UIScrollView *)scrollView;
  • 獲取圖像動(dòng)畫(huà)權(quán)限:是否開(kāi)啟圖片動(dòng)畫(huà)(默認(rèn)值為 NO):
- (BOOL)emptyDataSetShouldAnimateImageView:(UIScrollView *)scrollView {
    return YES;
}
  • 空白數(shù)據(jù)集 視圖被點(diǎn)擊 時(shí)觸發(fā)該方法:
- (void)emptyDataSet:(UIScrollView *)scrollView didTapView:(UIView *)view {
    // 處理視圖點(diǎn)擊事件...
}
  • 空白數(shù)據(jù)集 按鈕被點(diǎn)擊時(shí) 觸發(fā)該方法:
- (void)emptyDataSet:(UIScrollView *)scrollView didTapButton:(UIButton *)button {
    // 處理按鈕點(diǎn)擊事件...
}
  • 空白頁(yè)將要出現(xiàn)
- (void)emptyDataSetWillAppear:(UIScrollView *)scrollView {
  // 如果你的空白占位圖與需求向左,發(fā)生偏移,可如下設(shè)置:
  self.tableView.contentOffset = CGPointZero;
}
  • 空白頁(yè)已經(jīng)出現(xiàn)
- (void)emptyDataSetDidAppear:(UIScrollView *)scrollView;
  • 空白頁(yè)將要消失
- (void)emptyDataSetWillDisappear:(UIScrollView *)scrollView;
  • 空白頁(yè)已經(jīng)消失
- (void)emptyDataSetDidDisappear:(UIScrollView *)scrollView;

刷新布局

如果你需要刷新空白頁(yè)面布局,只需調(diào)用:

[self.tableView reloadData];

或者

[self.collectionView reloadData];

這取決于你用的是哪一個(gè)視圖(UITableView/UICollectionView)。

強(qiáng)制布局更新

你還可以調(diào)用 [self.tableView reloadEmptyDataSet] 以使當(dāng)前空白頁(yè)面布局無(wú)效,并觸發(fā)布局更新,繞過(guò) -reloadData 方法。 如果你的數(shù)據(jù)源上有很多邏輯處理,當(dāng)你不需要或者想避免調(diào)用 -reloadData 方法時(shí)這可能很有用。 當(dāng)使用 UIScrollView 滾動(dòng)視圖時(shí),[self.scrollView reloadEmptyDataSet] 是刷新內(nèi)容的唯一方法。

彩蛋

我在 GitHub 上傳了相關(guān)代碼的實(shí)現(xiàn)源碼 iOS-Samples/HQLTableViewDemo,有需要的可以去看看,當(dāng)然,你也可以直接去下載 DZNEmptyDataSet 中的 Demo,相信你能得到啟發(fā)。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點(diǎn)贊按鈕進(jìn)度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 46,872評(píng)論 22 665
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,025評(píng)論 25 708
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,198評(píng)論 4 61
  • 本次開(kāi)發(fā)環(huán)境為: 系統(tǒng):Linux Mint 18 JDK:1.8 開(kāi)發(fā)工具:IntelliJ IDEA 2017...
    cn華少閱讀 911評(píng)論 0 0
  • 在進(jìn)行模糊的時(shí)候,可以先對(duì)原始圖片進(jìn)行壓縮,然后選擇一個(gè)合適的方式進(jìn)行模糊。效果如下: 1、處理圖片 縮放、旋轉(zhuǎn)圖...
    i冰點(diǎn)閱讀 1,654評(píng)論 0 1