reloadData 结束回调

在使用UITableview或者UICollectionView时候或许会碰到这种情况:在reloadData执行完成时候进行某些操作,但是apple提供的方法并不支持,查阅互联网资料大概得三种种方法
一:采用dispatch_anync();二:采用UIView的animation;三:采用dispatch_after()。
经过测试,这三种方方法在reload过程中存在耗时操作情况都没办法做的很准确,甚至先于reload结束。
这边介绍一种新的方式,原理是在reload发起之前监听content
size的设置,并且给个很短超时时间,一旦超时表示reload完成,执行回调。具体如下:

#import 
#import 

//前一次 -[ reloadData:] 结束前再次 -[ reloadData:] 将抛弃前次结果
//注:-[ reloadData:]不会产生循环应用

@interface UITableView (reload)
-(void)reloadData:(void(^)(void))complete;
@end

@interface UICollectionView (reload)
-(void)reloadData:(void(^)(void))complete;
@end
#import "ReloadedData.h"
#import 


#pragma mark - objc/runtime
@interface _WeakObject : NSObject
@property (nonatomic, weak)id weak;
@end
@implementation _WeakObject
@end

static id get_attribute_strong(id object, const void * key)
{
    return objc_getAssociatedObject(object, key);
}

static void set_attribute_strong(id object, const void * key, id value)
{
    objc_setAssociatedObject(object, key, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

static id get_attribute_weak(id object, const void * key)
{
    _WeakObject *weak = get_attribute_strong(object, key);
    return weak.weak;
}

static void set_attribute_weak(id object, const void * key, id value)
{
    _WeakObject *weak;
    if (value)
    {
        weak = get_attribute_strong(object, key);
        if (!weak) {
            weak = [_WeakObject new];
        }
        weak.weak = value;
    }
    set_attribute_strong(object, key, weak);
}


#pragma mark - _ReloadedData
@interface _ReloadedData : NSObject
@property (nonatomic, strong) UIScrollView *view;
@property (nonatomic, copy) void(^callback)(void);
@end

@implementation _ReloadedData

static _ReloadedData* build_reloadedData(UIScrollView *view, void(^complete)(void))
{
    __block _ReloadedData *reloadedData = [[_ReloadedData alloc] init];
    reloadedData.view = view;
    reloadedData.callback = ^{
        reloadedData = nil;
        complete();
    };
    return reloadedData;
}

-(void)setView:(UIScrollView *)view
{
    static NSString * const kContentSize = @"contentSize";
    [_view removeObserver:self forKeyPath:kContentSize];
    _view = view;
    [_view addObserver:self forKeyPath:kContentSize options:NSKeyValueObservingOptionNew context:nil];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    [self performSelector:@selector(onTimeOut) withObject:nil afterDelay:0.05];
}

-(void)onTimeOut
{
    void(^callback)(void) = self.callback;
    [self scrap];
    callback();
}

-(void)scrap
{
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    self.callback = nil;
    self.view = nil;
}

-(void)dealloc
{
    [self scrap];
}

@end


#pragma mark - UITableView UICollectionView
@interface UIScrollView ()
- (id)dataSource;
- (void)reloadData;
@end

@implementation UIScrollView (TableViewOrCollectionView)

-(_ReloadedData *)rdObserver
{
    return get_attribute_weak(self, @selector(rdObserver));
}

-(void)setRdObserver:(_ReloadedData *)rdObserver
{
    set_attribute_weak(self, @selector(rdObserver), rdObserver);
}

-(void)reloadData:(void(^)(void))complete
{
    if (complete && self.delegate && self.dataSource)
    {
        [self.rdObserver scrap];
        self.rdObserver = build_reloadedData(self, complete);
    }
    [self reloadData];
}

@end

注意:reloadData方法仅仅是渲染当前显示部分的内容,内容很多的情况貌似并不能得到整个的contentSize。这边的回调也在这个条件内。
回调不需要处理循环引用,有兴趣看一下代码。
欢迎反馈意见和bug

你可能感兴趣的:(reloadData 结束回调)