ken*_*000 108 iphone uitableview ios
有没有办法找出什么时候UITableView
从数据源请求数据?
相关视图控制器()的viewDidLoad
/ viewWillAppear
/ viewDidAppear
方法UITableViewController
都没有在这里使用,因为它们都过早发生.它们(完全可以理解)都不保证对数据源的查询暂时完成(例如,直到滚动视图).
一个解决办法,我发现是调用reloadData
的viewDidAppear
,因为当reloadData
返回时,表视图是保证完成尽可能多的,因为它需要暂时查询的数据源.
但是,这看起来相当令人讨厌,因为我认为这是因为reloadData
它首次加载时会导致数据源被要求提供相同的信息两次(一次是自动的,一次是因为调用).
我想要这样做的原因是我想要保留 - 的滚动位置UITableView
- 但是直到像素级别,而不仅仅是最近的行.
当恢复滚动位置(使用scrollRectToVisible:animated:
)时,我需要表视图中已经有足够的数据,否则scrollRectToVisible:animated:
方法调用什么都不做(如果您将调用单独放在任何一个中viewDidLoad
,则会发生这种情况,viewWillAppear
或者viewDidAppear
).
Eri*_*AND 60
由于自编写答案以来对UITableView实现进行了一些更改,因此这个答案似乎不再起作用了.看到此评论:当UITableView完成询问数据时收到通知?
我一直在玩这个问题几天,认为继承UITableView
的reloadData
是最好的方法:
- (void)reloadData {
NSLog(@"BEGIN reloadData");
[super reloadData];
NSLog(@"END reloadData");
}
Run Code Online (Sandbox Code Playgroud)
reloadData
在表完成重新加载其数据之前不会结束.因此,当第二个NSLog
被触发时,表视图实际上已经完成了对数据的请求.
我已经将子UITableView
方法发送给委托之前和之后reloadData
.它就像一个魅力.
iPr*_*abu 52
我确实在我的应用程序中有相同的场景,并且认为会将我的答案发布给你们,因为这里提到的其他答案对我来说对iOS7及更高版本不起作用
最后,这是我唯一能解决的问题.
[yourTableview reloadData];
dispatch_async(dispatch_get_main_queue(),^{
NSIndexPath *path = [NSIndexPath indexPathForRow:yourRow inSection:yourSection];
//Basically maintain your logic to get the indexpath
[yourTableview scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionTop animated:YES];
});
Run Code Online (Sandbox Code Playgroud)
Swift更新:
yourTableview.reloadData()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let path : NSIndexPath = NSIndexPath(forRow: myRowValue, inSection: mySectionValue)
//Basically maintain your logic to get the indexpath
yourTableview.scrollToRowAtIndexPath(path, atScrollPosition: UITableViewScrollPosition.Top, animated: true)
})
Run Code Online (Sandbox Code Playgroud)
那么这是如何工作的.
基本上当你重新加载时,主线程变得很忙,所以当我们执行调度异步线程时,该块将等到主线程完成.因此,一旦完成了tableview的加载,主线程将完成,因此它将调度我们的方法块
在iOS7和iOS8中测试过,效果很棒;)
iOS9的更新:这也适用于iOS9.我在github中创建了一个示例项目作为POC. https://github.com/ipraba/TableReloadingNotifier
我在这附上我测试的截图.
经测试的环境:来自Xcode7的iOS9 iPhone6模拟器
ban*_*isa 25
编辑:这个答案实际上不是一个解决方案.它似乎首先起作用,因为重新加载可以非常快地发生,但实际上完成块不一定在数据完全重新加载后被调用 - 因为reloadData不会阻塞.您应该寻找更好的解决方案.
为了扩展@Eric MORAND的答案,让我们进入一个完成块.谁不喜欢一个块?
@interface DUTableView : UITableView
- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock;
@end
Run Code Online (Sandbox Code Playgroud)
和...
#import "DUTableView.h"
@implementation DUTableView
- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock {
[super reloadData];
if(completionBlock) {
completionBlock();
}
}
@end
Run Code Online (Sandbox Code Playgroud)
用法:
[self.tableView reloadDataWithCompletion:^{
//do your stuff here
}];
Run Code Online (Sandbox Code Playgroud)
小智 11
reloadData只询问可见单元格的数据.说,当指定表的部分加载时要通知,请挂钩该tableView: willDisplayCell:
方法.
- (void) reloadDisplayData
{
isLoading = YES;
NSLog(@"Reload display with last index %d", lastIndex);
[_tableView reloadData];
if(lastIndex <= 0){
isLoading = YES;
//Notify completed
}
- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row >= lastIndex){
isLoading = NO;
//Notify completed
}
Run Code Online (Sandbox Code Playgroud)
Mar*_*ski 10
这是我的解决方案.100%工作并在许多项目中使用.它是一个简单的UITableView子类.
@protocol MyTableViewDelegate<NSObject, UITableViewDelegate>
@optional
- (void)tableViewWillReloadData:(UITableView *)tableView;
- (void)tableViewDidReloadData:(UITableView *)tableView;
@end
@interface MyTableView : UITableView {
struct {
unsigned int delegateWillReloadData:1;
unsigned int delegateDidReloadData:1;
unsigned int reloading:1;
} _flags;
}
@end
@implementation MyTableView
- (id<MyTableViewDelegate>)delegate {
return (id<MyTableViewDelegate>)[super delegate];
}
- (void)setDelegate:(id<MyTableViewDelegate>)delegate {
[super setDelegate:delegate];
_flags.delegateWillReloadData = [delegate respondsToSelector:@selector(tableViewWillReloadData:)];
_flags.delegateDidReloadData = [delegate respondsToSelector:@selector(tableViewDidReloadData:)];
}
- (void)reloadData {
[super reloadData];
if (_flags.reloading == NO) {
_flags.reloading = YES;
if (_flags.delegateWillReloadData) {
[(id<MyTableViewDelegate>)self.delegate tableViewWillReloadData:self];
}
[self performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f];
}
}
- (void)finishReload {
_flags.reloading = NO;
if (_flags.delegateDidReloadData) {
[(id<MyTableViewDelegate>)self.delegate tableViewDidReloadData:self];
}
}
@end
Run Code Online (Sandbox Code Playgroud)
它与Josh Brown的解决方案类似,只有一个例外.performSelector方法不需要延迟.无论reloadData
需要多长时间.tableViewDidLoadData:
在tableView
完成询问时总是开火dataSource
cellForRowAtIndexPath
.
即使您不想要子类,UITableView
也可以简单地调用,[performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f]
并且在表完成重新加载后立即调用您的选择器.但是你应该确保每次调用时只调用一次选择器reloadData
:
[self.tableView reloadData];
[self performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f];
Run Code Online (Sandbox Code Playgroud)
请享用.:)
这是一个稍微不同的问题的答案:我需要知道什么时候UITableView
也完成了呼叫cellForRowAtIndexPath()
.我layoutSubviews()
继承了(感谢@Eric MORAND)并添加了一个委托回调:
SDTableView.h:
@protocol SDTableViewDelegate <NSObject, UITableViewDelegate>
@required
- (void)willReloadData;
- (void)didReloadData;
- (void)willLayoutSubviews;
- (void)didLayoutSubviews;
@end
@interface SDTableView : UITableView
@property(nonatomic,assign) id <SDTableViewDelegate> delegate;
@end;
Run Code Online (Sandbox Code Playgroud)
SDTableView.m:
#import "SDTableView.h"
@implementation SDTableView
@dynamic delegate;
- (void) reloadData {
[self.delegate willReloadData];
[super reloadData];
[self.delegate didReloadData];
}
- (void) layoutSubviews {
[self.delegate willLayoutSubviews];
[super layoutSubviews];
[self.delegate didLayoutSubviews];
}
@end
Run Code Online (Sandbox Code Playgroud)
用法:
MyTableViewController.h:
#import "SDTableView.h"
@interface MyTableViewController : UITableViewController <SDTableViewDelegate>
@property (nonatomic) BOOL reloadInProgress;
Run Code Online (Sandbox Code Playgroud)
MyTableViewController.m:
#import "MyTableViewController.h"
@implementation MyTableViewController
@synthesize reloadInProgress;
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if ( ! reloadInProgress) {
NSLog(@"---- numberOfSectionsInTableView(): reloadInProgress=TRUE");
reloadInProgress = TRUE;
}
return 1;
}
- (void)willReloadData {}
- (void)didReloadData {}
- (void)willLayoutSubviews {}
- (void)didLayoutSubviews {
if (reloadInProgress) {
NSLog(@"---- layoutSubviewsEnd(): reloadInProgress=FALSE");
reloadInProgress = FALSE;
}
}
Run Code Online (Sandbox Code Playgroud)
注意:
因为这是一个子类,UITableView
它已经有一个指向的委托属性,MyTableViewController
所以不需要添加另一个."@dynamic delegate"告诉编译器使用此属性.(这是一个描述这个的链接:http://farhadnoorzay.com/2012/01/20/objective-c-how-to-add-delegate-methods-in-a-subclass/)
该UITableView
物业MyTableViewController
必须更改为使用新SDTableView
类.这是在Interface Builder Identity Inspector中完成的.选择UITableView
内部UITableViewController
并将其"自定义类"设置为SDTableView
.
我已经发现了类似以获取更改通知的东西contentSize
的TableView
.我认为这也应该在这里工作,因为contentSize也随着加载数据而变化.
试试这个:
在viewDidLoad
写,
[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionPrior context:NULL];
Run Code Online (Sandbox Code Playgroud)
并将此方法添加到viewController:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"contentSize"]) {
DLog(@"change = %@", change.description)
NSValue *new = [change valueForKey:@"new"];
NSValue *old = [change valueForKey:@"old"];
if (new && old) {
if (![old isEqualToValue:new]) {
// do your stuff
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可能需要在检查更改时稍作修改.这虽然对我有用.
干杯! :)
我相信我也有类似的事情。我添加了一个 BOOL 作为实例变量,它告诉我偏移量是否已恢复并在-viewWillAppear:
. 当它还没有恢复时,我在该方法中恢复它并设置 BOOL 来指示我确实恢复了偏移量。
这是一种黑客攻击,可能可以做得更好,但这目前对我有用。