2013-04-15 113 views
2

我正在从Firebase加载列表数据并填充UITableView。虽然我看到从我的firebase实例调用的快照,但它们不会填充表,而本地同步NSMutableArray会显示内容。如何让我的UITableView等待Firebase数据到达那里?异步加载 - UITableView和Firebase

下面的代码:

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    handles = [[NSMutableArray alloc] initWithObjects:@"Item No. 1", @"Item No. 2", @"Item No. 3", @"Item No. 4", @"Item No. 5", @"Item No. 6", nil]; 
    [self loadDataFromFirebase]; 
    NSLog(@"%lu", (unsigned long)[handles count]); 
    self.sampleTableView.delegate = self; 
    self.sampleTableView.dataSource = self; 
} 

-(void)loadDataFromFirebase 
{ 
    Firebase* listRef = [[Firebase alloc] initWithUrl:@"https://wheresthatfoodtruck.firebaseIO.com/foodtrucks"]; 
    [listRef observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) { 
     NSLog(@"%@", snapshot.value); 
     [handles addObject:snapshot.value]; 
    }]; 
} 

手柄打印出的项目数,但如果我删除的测试数据,[拉手计数]变为零和的UITableView的加载速度更快,然后从火力地堡获取数据。非常感谢 - 我真的很喜欢Firebase,它是一个很棒的平台,我希望我能通过这个问题。

+0

你在哪里更新你的餐桌?作为一个快速修复尝试添加:'[weakSelf.sampleTableView reloadData]'在块的末尾('weakSelf'应该在块外定义为'__block __weak id weakSelf = self;' –

+0

截至目前,我只是拉下json数据,我试过在每个snapshot.value调用后调用[simpleTableView reloadData],但没有运气。弱类型有什么作用? –

+0

我有一个错误,其中sampleTableView不是weakSelf的属性。自我的副本可以访问所有合成的属性? –

回答

4

对于Firebase,您必须牢记所有代码(特别是实时更新的块)都是异步的。例如,在原来的问题的意见,以及在工作中,各地的答案,这样的代码:

[self loadDataFromFirebase]; 
[self.sampleTableView reloadData]; 

...不能从上到下阅读;在正在调用异步块中正在填充的数据(或正在更新的视图)和正在调用的数据之间将存在争用条件;也许单元格连接是不稳定的,或者GCD正在优化一个事物,等等。

Firebase记住的第二件事是,对于像child_added这样的事件,可能从来没有任何“完整”的概念 - 数据不断变换和更新是完全可能的;再次,预计reloadData何时会被叫?马上?第一个孩子之后?最后一个孩子?这样做的含义是,如果添加新数据,即使解决方法也会产生您可能不期望的行为(即它看起来好像数据没有显示,因为reloadData将永远不会再被调用。 )

您可以采取的一种方法是在数据更改时直接更新您的视图;所以,你的格挡会再看看这样的:

[listRef observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) { 
    NSLog(@"%@", snapshot.value); 
    [handles addObject:snapshot.value]; 
    [self.sampleTableView reloadData]; 
}]; 

另一种方法可能是使用火力地堡专门更新您的内部模型,并使用常规的Objective-C的方法(KVO,通知,MVC等)来分离数据并且关注在您的模型的直接变化之外重新提出意见。

0

想通了。而是试图在viewDidLoad中查询数据库,在这种情况下,视图已经作为一个框架出现了,我将查询移动到nib加载时,但在框架变为可见之前。下面的代码,在扩大:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 
{ 
    //link the CloudViewController with its View 
    NSBundle *appBundle = [NSBundle mainBundle]; 

    self = [super initWithNibName:@"FoodTruckViewController" bundle:appBundle]; 
    if (self) { 
     self.sampleTableView.delegate = self; 
     self.sampleTableView.dataSource = self; 
     [self loadDataFromFirebase]; 
     [self.sampleTableView reloadData]; 
    } 
    return self; 
} 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    [self.sampleTableView reloadData]; 
} 

-(void)loadDataFromFirebase 
{ 
    handles = [[NSMutableArray alloc] init]; 
    Firebase* listRef = [[Firebase alloc] initWithUrl:@"https://wheresthatfoodtruck.firebaseIO.com/foodtrucks"]; 
    [listRef observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) { 
     NSLog(@"%@", snapshot.value); 
     [handles addObject:snapshot.value]; 
    }]; 
} 


- (void)didReceiveMemoryWarning 
{ 
    [super didReceiveMemoryWarning]; 
    // Dispose of any resources that can be recreated. 
} 

#pragma mark - Table view data source 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{ 
    return 1; 
} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    return [handles count]; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *CellIdentifier = @"Cell"; 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
    if (cell == nil) { 
     cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; 
    } 
[cell.textLabel setText:[handles objectAtIndex:indexPath.row]]; 

    return cell; 
} 

#pragma mark - Table view delegate 

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    // Navigation logic may go here. Create and push another view controller. 
    /* 
    <#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:@"<#Nib name#>" bundle:nil]; 
    // ... 
    // Pass the selected object to the new view controller. 
    [self.navigationController pushViewController:detailViewController animated:YES]; 
    */ 
} 

@end 

这个版本的代码填充表变得可见之前,但不会在实时变化表进行更新。我认为下一步将调用viewDidLoad方法中的ChildChanged函数窗体firebase来更新Firebase方面的所有更改。

+0

此解决方法仍包含从Firebase触发的事件与正在更新的视图之间的竞争条件。不保证视图会在从Firebase加载数据之后“更新”,除非您更新块中的视图*。 – Vikrum

+0

对于滞后抱歉,我从你对我的问题的其他答复中解决了这个问题。我想我现在需要为移动设备和服务器之间的延迟添加加载条。 –