iOS无数据视图DZNEmptyDataSet的使用和原理

29 篇文章 0 订阅
地址:

功能
当tableview或者collectionView没有数据时,展示指定无数据视图

使用步骤
只需要遵守协议
DZNEmptyDataSetSource和DZNEmptyDataSetDelegate

成为代理
self.tableView.emptyDataSetSource = self;
self.tableView.emptyDataSetDelegate = self;

实现代理方法,如:
//无数据图片
- (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView
//无数据标题
- (NSAttributedString *)titleForEmptyDataSet:(UIScrollView *)scrollView
还有其他很多代理方法,可以在里面找自己需要的

原理
创建一个scrollview的分类UIScrollView+EmptyDataSet

在数据源的set方法setEmptyDataSetSource中通过objc_setAssociatedObject方法将控制器赋值给自身属性

同时调用自身的方法swizzleIfPossible

- (void)setEmptyDataSetSource:(id<DZNEmptyDataSetSource>)datasource

 {

    if (!datasource || ![self dzn_canDisplay]) {

        [self dzn_invalidate];

    }

    

 objc_setAssociatedObject(self, kEmptyDataSetSource, [[DZNWeakObjectContainer alloc] initWithWeakObject:datasource], OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    

    // We add method sizzling for injecting -dzn_reloadData implementation to the native -reloadData implementation

    [self swizzleIfPossible:@selector(reloadData)];

    

    // Exclusively for UITableView, we also inject -dzn_reloadData to -endUpdates

    if ([self isKindOfClass:[UITableView class]]) {

        [self swizzleIfPossible:@selector(endUpdates)];

    }

}


swizzleIfPossible方法中通过method_setImplementation把tableView或者collectionView的reloadData方法替换成dzn_reloadEmptyDataSet(具体替换的实现可以看dzn_original_implementation方法

- (void)swizzleIfPossible:(SEL)selector

{

    // Check if the target responds to selector

    if (![self respondsToSelector:selector]) {

        return;

    }

    

    // Create the lookup table

    if (!_impLookupTable) {

        _impLookupTable = [[NSMutableDictionary alloc] initWithCapacity:3]; // 3 represent the supported base classes

    }

    

    // We make sure that setImplementation is called once per class kind, UITableView or UICollectionView.

    for (NSDictionary *info in [_impLookupTable allValues]) {

        Class class = [info objectForKey:DZNSwizzleInfoOwnerKey];

        NSString *selectorName = [info objectForKey:DZNSwizzleInfoSelectorKey];

        

        if ([selectorName isEqualToString:NSStringFromSelector(selector)]) {

            if ([self isKindOfClass:class]) {

                return;

            }

        }

    }

    

    Class baseClass = dzn_baseClassToSwizzleForTarget(self);

    NSString *key = dzn_implementationKey(baseClass, selector);

    NSValue *impValue = [[_impLookupTable objectForKey:key] valueForKey:DZNSwizzleInfoPointerKey];

    

    // If the implementation for this class already exist, skip!!

    if (impValue || !key || !baseClass) {

        return;

    }

    

    // Swizzle by injecting additional implementation

    Method method = class_getInstanceMethod(baseClass, selector);

    IMP dzn_newImplementation = method_setImplementation(method, (IMP)dzn_original_implementation);

    

    // Store the new implementation in the lookup table

    NSDictionary *swizzledInfo = @{DZNSwizzleInfoOwnerKey: baseClass,

                                   DZNSwizzleInfoSelectorKey: NSStringFromSelector(selector),

                                   DZNSwizzleInfoPointerKey: [NSValue valueWithPointer:dzn_newImplementation]};

    

    [_impLookupTable setObject:swizzledInfo forKey:key];

}


void dzn_original_implementation(id self, SEL _cmd)

{

    // Fetch original implementation from lookup table

    Class baseClass = dzn_baseClassToSwizzleForTarget(self);

    NSString *key = dzn_implementationKey(baseClass, _cmd);

    

    NSDictionary *swizzleInfo = [_impLookupTable objectForKey:key];

    NSValue *impValue = [swizzleInfo valueForKey:DZNSwizzleInfoPointerKey];

    

    IMP impPointer = [impValue pointerValue];

    

    // We then inject the additional implementation for reloading the empty dataset

    // Doing it before calling the original implementation does update the 'isEmptyDataSetVisible' flag on time.

    [self dzn_reloadEmptyDataSet];

    

    // If found, call original implementation

    if (impPointer) {

        ((void(*)(id,SEL))impPointer)(self,_cmd);

    }

}



dzn_reloadEmptyDataSet方法中,通过判断数据个数(数据个数通过numberOfSectionsInTableView和tableView:numberOfRowsInSection:来获取)来决定是否显示emptyDataView,如tableView的判断依据

// UITableView support

    if ([self isKindOfClass:[UITableView class]]) {

        

        UITableView *tableView = (UITableView *)self;

        id <UITableViewDataSource> dataSource = tableView.dataSource;

        

        NSInteger sections = 1;

        

        if (dataSource && [dataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]) {

            sections = [dataSource numberOfSectionsInTableView:tableView];

        }

        

        if (dataSource && [dataSource respondsToSelector:@selector(tableView:numberOfRowsInSection:)]) {

            for (NSInteger section = 0; section < sections; section++) {

                items += [dataSource tableView:tableView numberOfRowsInSection:section];

            }

        }

    }


代码太多,这里就讲个大概思路

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值