在UITableView中处理UIButton照片网格的最有效方法是什么?



我有一个正在开发的iOS应用程序,它通过JSON请求从MySQL数据库中获取一组照片URL。一旦我有了这些照片和相关信息,我就用它来填充UITableView的数据源。我想创建一个UI按钮网格,由照片组成,每行4个。这个当前的代码是有效的,但是它非常慢,当我在表格中滚动时,我的手机/模拟器会冻结。只有几行的表可以正常工作,但一旦我达到10行或更多行,它就会减慢速度,几乎崩溃。我是iOS和objective-c的新手,所以我认为这在我的代码中效率低下。有什么建议吗?谢谢

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger row = [indexPath row];
static NSString *CompViewCellIdentifier = @"CompViewCellIdentifier";
UITableViewCell *cell = [tableView
                         dequeueReusableCellWithIdentifier: CompViewCellIdentifier];
if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CompViewCellIdentifier] autorelease];
}
// The photo number in the photos array that we'll need to start off with.
NSUInteger photoNumber = (row * 4);
// Assemble the array of all 4 photos we'll need for this table row (for this cell).
NSMutableArray *rowPhotos = [[[NSMutableArray alloc] initWithObjects:[self.photos objectAtIndex:photoNumber], nil] retain];
NSInteger counter = 1;
while ([self.photos count] > photoNumber+counter && counter<4) {
    [rowPhotos addObject:[self.photos objectAtIndex:photoNumber+counter]];
    counter = counter+1;
}
NSLog(@"The rowPhotos array: %@", rowPhotos);
for (int i=0; i<[rowPhotos count]; i++) {
    // Set which photo we're dealing with for this iteration by grabbing it from our rowPhotos array we assembled. Use i as the index.
    NSDictionary *photoRow = [[NSDictionary alloc] initWithDictionary:[rowPhotos objectAtIndex:i]];
    // Get the photo.
    NSString *photoPath = [[NSString alloc] initWithFormat:@"http://localhost/photorious%@", [photoRow objectForKey:@"path"]];
    NSURL *url = [NSURL URLWithString: photoPath];
    [photoPath release];
    UIImage *cellPhoto = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:url]];
    // Figure out the container size and placement.
    int xCoordinate = ((i*70)+8*(i+1));
    CGRect containerRect = CGRectMake(xCoordinate, 0, 70, 70);
    // Create the Button
    UIButton *cellPhotoButton = [UIButton buttonWithType:UIButtonTypeCustom];
    [cellPhotoButton setFrame:containerRect];
    [cellPhotoButton setBackgroundImage:cellPhoto forState:UIControlStateNormal];
    [cellPhotoButton setTag:(NSInteger)[photoRow objectForKey:@"id"]];
    // Add the button to the cell
    [cell.contentView addSubview:cellPhotoButton];
    // Add the action for the button.
    [cellPhotoButton addTarget:self 
                        action:@selector(viewPhoto:)
              forControlEvents:UIControlEventTouchUpInside];
    [cellPhoto release];
}
[rowPhotos release];
return cell;
}

这很慢,因为您在tableView:cellForRowAtIndexPath:中执行所有操作。tableView:cellForRowAtIndexPath:经常被调用,尤其是每次需要在表视图中显示单元格时,包括滚动表视图时。因此,这种方法需要快速,并且不阻塞(尤其是不要进行同步下载!)

此外,您没有正确使用表视图单元格的可重用性。每次为每个单元重新创建内容(子视图)时,这会大大降低性能。

  • 当您的单元格从上一个单元格重复使用时(将其视为"回收"),您不能重做所有内容,尤其是不能像单元格本身已经存在的那样重新添加每个子视图,因为它已经被重复使用,并且不是一个干净的新视图
  • 相反,当dequeueReusableCellWithIdentifier:返回一个单元格(=以前创建但不再使用的旧单元格,以便您可以"回收"/重用它)时,您应该只更改不同单元格的不同。在您的示例中,通常您只会更改显示的4个图像,但不会重新创建UIImageView,也不会将它们添加为子视图(因为这些子视图已经存在),也不会重新影响目标/操作
  • 当您使用alloc/initWithReuseIdentifier:/autorelease创建一个全新的单元格时,您只需要创建UIImageView,为它们添加一个目标/操作,设置它们的框架并将它们添加为子视图

此外,您可以直接在tableView:cellForRowAtIndexPath:中从网络获取图像,此外还可以同步获取图像(这意味着它会阻止您的应用程序,直到它完成从网络下载图像!!)。相反,在tableView:cellForRowAtIndexPath:之前(例如,当加载应用程序时)进行异步下载,并将其存储在本地(例如,在NSArray或类似的sthg中),并且只在tableView:cellForRowAtIndexPath:中获取本地已下载的映像。

如果你是iOS编程的新手,那么你尝试做的事情并不是最好的主意。你想做的可能看起来很容易,但它暗示了异步下载、应用程序的MVC设计以及在视图中显示图像之前从模型中的网络中预取图像等概念,提供了下载完成后更新表视图的方法,以及表视图中单元重用的基本概念。

请先阅读TableView编程指南,然后再继续阅读。它详细地解释了它,真的值得一读。另请参阅苹果公司的LazyTableImages示例代码,该代码解释了如何轻松地在表视图中加载图像(意味着在需要时异步加载图像),以及URL加载编程指南,该指南解释了如何异步下载数据。

如果你想做你解释的事情,这些指南和示例非常值得一读。在网上也有很多类可以做网格视图,其中一个是我的工作(OHGridView),但在继续之前,你需要先了解上面和提到的指南中解释的基础知识。

最新更新