2

我正在开发一个从 Firebase 加载列表数据并填充 UITableView 的项目。虽然我看到从我的 firebase 实例调用快照,但它们不会填充表,而本地同步 NSMutableArray 显示内容。如何让我的 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];
    }];
}

Handles 打印出项目编号,但如果我删除该测试数据,[Handles count] 变为零,并且 UITableView 的加载速度比从 Firebase 获取数据要快。非常感谢 - 我真的很喜欢 Firebase,它是一个很棒的平台,我希望我能解决这个问题。

4

2 回答 2

4

使用 Firebase,您必须记住所有代码(尤其是实时更新的块)都是异步的。例如,在原始问题的评论以及解决方法中,代码如下:

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

...无法从上到下阅读;异步块中正在填充的数据(或正在更新的视图)与被调用之间将存在竞争条件;reloadData可能细胞连接不稳定,或者 GCD 正在优化一件事而不是另一件事,等等。

使用 Firebase 时要记住的第二件事是,对于像这样的事件child_added,可能永远不会有任何“完整”的概念——数据完全有可能不断变化和更新;再说一次,人们什么时候会被叫到reloadData?马上?第一个孩子之后?最后一个孩子?这样做的含义是,如果添加了新数据,即使是变通方法也会产生您可能没有预料到的行为(即看起来好像数据没有出现,因为reloadData永远不会再次被调用。)

您可以在此处采用的一种方法是在数据更改时直接更新您的视图;因此,您的块将如下所示:

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

另一种方法可能是使用 Firebase 专门更新您的内部模型并使用常规的 Objective-C 方法(KVO、Notifications、MVC 等)来分离您的数据和视图关注点,以便在直接更改模型之外重新呈现视图。

于 2013-04-15T22:58:52.770 回答
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 方法中调用来自 firebase 的 ChildChanged 函数,以保持对 firebase 端的任何更改的更新。

于 2013-04-15T17:10:00.213 回答