在UITableView中处理UIButton照片网格最有效的方法是什么? [英] What's the most efficient way to handle a UIButton Photo Grid in a UITableView?

查看:140
本文介绍了在UITableView中处理UIButton照片网格最有效的方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个iOS应用程序,我正在使用从JSON数据库中获取一堆照片URL的JSON请求。一旦我有这些照片和相关信息,我用它来填充数据源为一个UITableView。我想创建一个UIButtons的网格,由照片制成,每行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];
}

//我们需要开始的照片数组中的照片编号。
NSUInteger photoNumber =(row * 4);

//汇编我们为这个表格行(对于这个单元格)需要的所有4张照片的数组。
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(@rowPhotos数组:%@,rowPhotos); (int i = 0; i< [rowPhotos count]; i ++){

//通过抓取它来设置我们正在处理的这张照片的照片我们组合的rowPhotos阵列。使用i作为索引。
NSDictionary * photoRow = [[NSDictionary alloc] initWithDictionary:[rowPhotos objectAtIndex:i]];

//获取照片。
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]];

//找出容器大小和位置。
int xCoordinate =((i * 70)+ 8 *(i + 1));
CGRect containerRect = CGRectMake(xCoordinate,0,70,70);

//创建按钮
UIButton * cellPhotoButton = [UIButton buttonWithType:UIButtonTypeCustom];
[cellPhotoButton setFrame:containerRect];
[cellPhotoButton setBackgroundImage:cellPhoto forState:UIControlStateNormal];
[cellPhotoButton setTag:(NSInteger)[photoRow objectForKey:@id]];

//将按钮添加到单元格
[cell.contentView addSubview:cellPhotoButton];

//添加按钮的动作。

[cellPhotoButton addTarget:self
action:@selector(viewPhoto :)
forControlEvents:UIControlEventTouchUpInside];

[cellPhoto release];
}

[rowPhotos发行];
返回单元格;
}


解决方案

在$ code> tableView中的所有内容:cellForRowAtIndexPath:。
tableView:cellForRowAtIndexPath:被称为真正的,特别是每次单元格需要显示在您的表格视图中时,包括当您滚动您的tableView时。因此,这种方法需要快速和非阻塞(特别是不要同步下载!)



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




  • 当您的单元格从前一个单元格重用时看到它是回收),你不能重做所有的东西,特别是你不能重新添加每个子视图,因为已经在单元格本身,因为它被重复使用,不是一个干净的新的!

  • 相反,当 dequeueReusableCellWithIdentifier:返回一个单元格(=以前创建但未使用的旧单元格,以便您可以循环/重复使用)你应该只改变从细胞到细胞的不同。在您的示例中,通常您只能更改显示的4个图像,但不要重新创建UIImageView,不将它们添加为子视图(因为这些子视图已存在),也不会重新定位目标/操作。

  • 您只需创建UIImageView,添加目标/动作,设置框架,并在创建新的单元格时将其添加为子视图,使用 alloc / initWithReuseIdentifier: / autorelease



此外,您正在直接在 tableView:cellForRowAtIndexPath:中从网络中获取图像,并同步(这意味着它会阻止您的应用程序,直到它从网络下载图像!)。
做一个异步下载,方法之前你的 tableView:cellForRowAtIndexPath:(当您的应用程序加载时),并存储在本地(在NSArray或sthg例如),并且只能在 tableView中获取本地已经下载的图像:cellForRowAtIndexPath:



如果您是iOS编程的新手,您尝试做的事情不是最好的开始。你想做什么可能看起来很容易,但它意味着像异步下载,应用程序的MVC设计和从模型中的网络预取图像之类的概念,然后在您的视图中显示之前,提供一种在下载完成时更新表格的方法,以及表格中单元重用的基本概念。



请阅读 TableView编程指南,然后再进一步。它详细解释它,真的值得一读。
另请参阅Apple的 LazyTableImages 示例代码这解释了如何懒惰地加载表视图中的图像(意味着在需要时异步加载图像),以及 URL加载编程指南,介绍如何进行异步下载数据。



这些指南和样本是<如果你想做你所解释的话,那么强>真的值得一读。网上还有很多类做网格视图,其中之一就是我的工作( OHGridView ),但您需要在进一步了解上述和上述指南之前了解基础知识。


I have an iOS app I'm working on that grabs a bunch of photo URLs from a MySQL database with a JSON request. Once I have these photos and related information, I use it to populate the datasource for a UITableView. I want to create a grid of UIButtons, made out of photos, 4 per row. This current code works, however it is wildly slow and my phone / simulator freezes right up as I scroll through the table. Tables with only a couple rows work fine, but once I reach 10 or more rows it slows right down and near crashes. I'm new to iOS and objective-c, so I'm assuming it's an inefficiency in my code. Any suggestions? Thanks!!

- (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;
}

解决方案

This is slow because you do everything in tableView:cellForRowAtIndexPath:. tableView:cellForRowAtIndexPath: Is called really ofter, especially each time a cell need to be displayed in your tableview, which includes when your are scrolling your tableView. Thus this method needs to be fast, and non-blocking (especially don't do synchronous downloads!)

Moreover your don't use the reusability of your tableview cells correctly. This drastically decrease performance as you recreate the content (subviews) for each cell each time.

  • When your cell is reused from a previous one (see it as being "recycled"), you must NOT redo everything, especially you must not re-add every subviews as there already are in the cell itself, as it has been reused and is not a clean new one!
  • Instead, when dequeueReusableCellWithIdentifier: returns a cell (= an old cell previously created but not used anymore so you can "recycle"/reuse it), you should only change what differs from cell to cell. In your example, typically you will only change the 4 images displayed, but don't recreate the UIImageView, neither add them to as a subview (as these subviews already exists) nor reaffect the target/action.
  • You only need to create the UIImageView, add them a target/action, set their frame and add them as a subview when your are creating a brand new cell, with alloc/initWithReuseIdentifier:/autorelease.

Moreover, you are fetching your images from the network directly in your tableView:cellForRowAtIndexPath:, and synchronously in addition (which means it blocks your application until it finished downloading the image from the net!!). Do an asynchronous download instead, way before your tableView:cellForRowAtIndexPath: (when your app is loaded for example) and store them locally (in an NSArray, or sthg similar for example), and only fetch the local, already downloaded image in your tableView:cellForRowAtIndexPath:.

The thing you are trying to do is not the greatest idea to begin with if you are new to iOS programming. What you wanna do may seem easy, but it implies concepts like asynchronous downloads, MVC design of your app and prefetching the images from the net in your model before displaying them in your view, provide a way to update the tableview when the download is done, and the basic concepts of cell reuse in tableviews.

DO read the TableView Programming Guide before going further. It explains it in details and it really worth reading. Also consult Apple's LazyTableImages sample code which explains how to load images in a tableview lazyly (meaning loading images asynchronously when they are needed), and the URL Loading Programming Guide which explains how to do asynchronous downloads of data.

These guides and samples are really worth reading if you want to do what you explain. There are also a lot of classes to do Grid Views on the net, one of them being my work (OHGridView), but you need to understand basics explained above and in the mentioned guides first before going further.

这篇关于在UITableView中处理UIButton照片网格最有效的方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆