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