iOS 為UITableView或UICollectionView添加空值界面

背景:

1.平常碼業(yè)務(wù)時(shí),會有在列表沒值時(shí)展示占位圖的需求,有時(shí)候需要展示圖片及文字,有時(shí)候我們還需要為占位圖添加點(diǎn)擊事件等。
2.在每次請求的時(shí)候手動調(diào)用代碼去添加占位圖太麻煩,也不現(xiàn)實(shí),對代碼質(zhì)量也有影響。
3.git上面也有相關(guān)的三方庫,但第三方庫一般會考慮各種使用場景,代碼量大,會對包體積造成影響。
4.此Demo已經(jīng)滿足正常需求,且使用非常方便,一句代碼即可。

使用示例

只需要在初始化列表控件的時(shí)候,加入以下一句代碼即可。

// image有值,title為nil時(shí),只展示圖片
// image為nil,title有值時(shí),只展示文字
// block為nil時(shí),則代表不需要點(diǎn)擊事件
[_tableView setEmptyViewWithImage:[UIImage imageNamed:@"noresult"] title:@"某有數(shù)據(jù)啊,點(diǎn)擊我再刷新一次看看?" eventBlock:^{
 NSLog(@"點(diǎn)擊了,去刷新嘍");
 }];

實(shí)現(xiàn)技術(shù):

1.Runtime:類別關(guān)聯(lián)屬性,添加成員變量、方法交換
2.枚舉(空值界面類型)、映射(對代理回調(diào)的監(jiān)聽)、Block(對點(diǎn)擊事件的回調(diào))

代碼:

UIScrollView+Empty.h
//
//  UIScrollView+Empty.h
//
//  Created by ylh on 2019/11/13.
//  Copyright ? 2019年 private. All rights reserved.
//

/*
 1、使用時(shí)只需調(diào)用此方法,即可在調(diào)用SEL: reloadData 時(shí)自動判斷是否展示空值界面
 2、更多定制可根據(jù)此實(shí)現(xiàn)方案進(jìn)行相對應(yīng)拓展  一般會對emptyView 進(jìn)行定制操作
 
 exp:
 
 [_tableView setEmptyViewWithImage:[UIImage imageNamed:@"noresult"] title:@"某有數(shù)據(jù)啊,點(diǎn)擊我再刷新一次看看?" eventBlock:^{
 NSLog(@"點(diǎn)擊了,去刷新嘍");
 }];
 
 */

#import <UIKit/UIKit.h>


/**
 空值界面類型

 - EmptyImage: 只顯示圖片
 - EmptyTitle: 只顯示文字
 - EmptyImageAndTitle: 顯示圖片和文字
 */
typedef NS_ENUM(NSInteger,EmptyType) {
    EmptyImage,
    EmptyTitle,
    EmptyImageAndTitle
};

/**
 點(diǎn)擊事件回調(diào)
 */
typedef void(^EmptyEventBlock)(void);

NS_ASSUME_NONNULL_BEGIN

@interface UIScrollView (Empty)
@property (nonatomic,strong)UIView *emptyView;
@property (nonatomic,copy)EmptyEventBlock emptyEventBlock;
/**
 設(shè)置空值界面
 根據(jù)傳入內(nèi)容決定展示風(fēng)格:  圖片、文字、文字和圖片、是否添加點(diǎn)擊事件

 @param image 展示圖片 可為空
 @param title 展示文字 可為空
 @param block 點(diǎn)擊事件block 可為空  為空則沒有點(diǎn)擊事件
 */
- (void)setEmptyViewWithImage:(UIImage *_Nullable)image title:(NSString *_Nullable)title eventBlock:(EmptyEventBlock _Nullable)block;
@end


@interface UITableView (Empty)

@end

@interface UICollectionView (Empty)

@end
NS_ASSUME_NONNULL_END

UIScrollView+Empty.m
//
//  UIScrollView+Empty.m
//
//  Created by ylh on 2019/11/13.
//  Copyright ? 2019年 private. All rights reserved.
//

#import "UIScrollView+Empty.h"
#import <objc/runtime.h>

static NSString *kEmptyViewKey = @"EmptyViewKey";
static NSString *kEmptyEventBlockKey = @"EmptyEventBlockKey";

@implementation UIScrollView (Empty)


- (void)setEmptyView:(UIView *)emptyView {
    objc_setAssociatedObject(self, &kEmptyViewKey, emptyView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIView *)emptyView {
    return objc_getAssociatedObject(self, &kEmptyViewKey);
}

- (void)setEmptyEventBlock:(EmptyEventBlock)emptyEventBlock {
    objc_setAssociatedObject(self, &kEmptyEventBlockKey, emptyEventBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (EmptyEventBlock)emptyEventBlock {
    return objc_getAssociatedObject(self, &kEmptyEventBlockKey);
}

- (void)setEmptyViewWithImage:(UIImage *)image title:(NSString *)title eventBlock:(EmptyEventBlock)block {
    if (!self.emptyView) {
        
        EmptyType type;
        if (image && !title) {
            type = EmptyImage;
        }else if (!image && title) {
            type = EmptyTitle;
        }else if (image && title) {
            type = EmptyImageAndTitle;
        }else{
            NSLog(@"參數(shù)錯誤,不能使用此功能");
            return;
        }
        
        UIButton *emptyBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        block ? (emptyBtn.enabled = YES) : (emptyBtn.enabled = NO);
        (emptyBtn.enabled = YES) ? [emptyBtn addTarget:self action:@selector(emptyClick:) forControlEvents:UIControlEventTouchUpInside]:nil;
        (emptyBtn.enabled = YES) ? (self.emptyEventBlock = block) : nil;
        
        switch (type) {
            case EmptyImage:
            {
                emptyBtn.frame = CGRectMake((self.frame.size.width-image.size.width)/2, (self.frame.size.height-image.size.height)/2, image.size.width, image.size.height);
                [emptyBtn setImage:image forState:UIControlStateNormal];
            }
                break;
            case EmptyTitle:
            {
                CGRect titleRect = [title boundingRectWithSize:CGSizeMake(self.frame.size.width, 20) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIFont systemFontOfSize:16.0f],NSFontAttributeName, nil] context:nil];
                emptyBtn.frame = CGRectMake((self.frame.size.width-titleRect.size.width)/2, (self.frame.size.height-titleRect.size.height)/2, titleRect.size.width, titleRect.size.height);
                [emptyBtn setTitle:title forState:UIControlStateNormal];
                [emptyBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
            }
                break;
            case EmptyImageAndTitle:
            {
                CGFloat btnWidth = 0;
                CGFloat btnHeight = 0;
                UIFont *titleFont = [UIFont systemFontOfSize:16.0f];
                CGRect titleRect = [title boundingRectWithSize:CGSizeMake(self.frame.size.width, 20) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:[NSDictionary dictionaryWithObjectsAndKeys:titleFont,NSFontAttributeName, nil] context:nil];
                titleRect.size.width <= image.size.width ? (btnWidth = image.size.width) : (btnWidth = titleRect.size.width);
                btnHeight = image.size.height + titleRect.size.height + 10;
                emptyBtn.frame = CGRectMake((self.frame.size.width-btnWidth)/2, (self.frame.size.height-btnHeight)/2, btnWidth, btnHeight);
                [emptyBtn setImage:image forState:UIControlStateNormal];
                [emptyBtn setTitle:title forState:UIControlStateNormal];
                [emptyBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
                [emptyBtn setImageEdgeInsets:UIEdgeInsetsMake(0, 0, 30, -titleRect.size.width)];
                [emptyBtn setTitleEdgeInsets:UIEdgeInsetsMake(btnHeight-20, -image.size.width, 0, 0)];
                emptyBtn.titleLabel.font = titleFont;
            }
                break;
            default:
                break;
        }
        self.emptyView = emptyBtn;
        [self addSubview:self.emptyView];
    }
    [self bringSubviewToFront:self.emptyView];
}

/// 更新界面,是否展示空值視圖
- (void)refreshEmptyView {
    NSInteger section = 1;
    NSInteger rows = 0;
    if ([self isKindOfClass:[UITableView class]]) {
        
        UITableView *tableView = (UITableView *)self;
        if (tableView.dataSource != nil && [tableView.dataSource respondsToSelector:@selector(tableView:numberOfRowsInSection:)]) {
            
            if ([tableView.dataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]) {
                section = [tableView.dataSource numberOfSectionsInTableView:tableView];
            }
            for (int i = 0; i < section; i++) {
                rows += [tableView.dataSource tableView:tableView numberOfRowsInSection:i];
            }
        }
    }else if ([self isKindOfClass:[UICollectionView class]]) {
        
        UICollectionView *collectionView = (UICollectionView *)self;
        if (collectionView.dataSource != nil && [collectionView.dataSource respondsToSelector:@selector(collectionView:numberOfItemsInSection:)]) {
            
            if ([collectionView.dataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]) {
                section = [collectionView.dataSource numberOfSectionsInCollectionView:collectionView];
            }
            for (int i = 0; i < section; i++) {
                rows += [collectionView.dataSource collectionView:collectionView numberOfItemsInSection:i];
            }
        }
    }
    
    if (rows == 0) {
        self.emptyView.hidden = NO;
        [self bringSubviewToFront:self.emptyView];
    }else{
        self.emptyView.hidden = YES;
        return;
    }
}

#pragma mark -- action

- (void)emptyClick:(UIButton *)sender {
    if (self.emptyEventBlock) {
        self.emptyEventBlock();
    }
}

#pragma mark -- swizzing
+ (void)hookClass:(Class)classObject originalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector {
    Class class = classObject;
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    
    BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    if (didAddMethod) {
        originalMethod = class_getInstanceMethod(class, originalSelector);
    }
    method_exchangeImplementations(swizzledMethod, originalMethod);
}

@end


@implementation UITableView (Empty)

+ (void)load {
    [super load];
    [self hookClass:[self class] originalSelector:@selector(reloadData) swizzledSelector:@selector(customReloadData)];
}
- (void)customReloadData {
    [self customReloadData];
    [self refreshEmptyView];
}

@end


@implementation UICollectionView (Empty)

+ (void)load {
    [super load];
    [self hookClass:[self class] originalSelector:@selector(reloadData) swizzledSelector:@selector(customReloadData)];
}

- (void)customReloadData {
    [self customReloadData];
    [self refreshEmptyView];
}

@end

如果對你幫助或者思路上的啟發(fā),幫忙點(diǎn)個(gè)贊唄!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容