UITableView和解析 - 滞后

时间:2014-10-17 20:33:03

标签: ios objective-c uitableview uiscrollview parse-platform

我在IOS 8 Objective-c iPhone应用程序中使用了Parse的PFQueryTableViewController。

我的列表包含一个标签和一个UIImageView,其中标签文本和图像都是从我的Parse核心中的一行下载的。我正在使用此代码来实现此目的:

- (PFQuery *)queryForTable
{
    PFQuery *query = [PFQuery queryWithClassName:@"Story"];

    return query;
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [[self objects] count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *simpleTableIdentifier = @"cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}

// Download the header image from parse
PFFile *imageFile = [object objectForKey:@"Image"];
[imageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
    if (!error) {
        UIImage *cellImage = [UIImage imageWithData:imageData];

        // Set the cellImage to the cell if it's not nil
        if (cellImage == nil) {
            // nil - do nothing
            NSLog(@"nil");
        } else {
            NSLog(@"not nil");
            // Set the image
            UIImageView *cellImageView = (UIImageView *)[cell viewWithTag:40];
            cellImageView.image = cellImage;
        }
    }
}];

// Configure the cell
UILabel *nameLabel = (UILabel*) [cell viewWithTag:10];
nameLabel.text = [object objectForKey:@"Title"];
nameLabel.textColor = [UIColor whiteColor];

// Make the cell transparent
cell.backgroundColor = [UIColor clearColor];
cell.backgroundView = [UIView new];
cell.selectedBackgroundView = [UIView new];

// Resize the cell
[cell sizeToFit];

return cell;
}

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Hide the tabBar and show the readButton
[self hideTabBar:self.tabBarController];

// Segue over to the viewing page
[self performSegueWithIdentifier:@"detailSegue" sender:self];

// Get the tapped cell
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

NSString *title = ((UILabel*) [cell viewWithTag:10]).text;

// Set selectedStory
MyManager *sharedManager = [MyManager sharedManager];
sharedManager.selectedStory = title;

// Set openedStory to YES as we opened a story
openedStory = YES;
}

此代码效果很好,但滚动有点滞后,我认为这是因为只要显示单元格,它就会下载图像。我想通过在本地创建一个图像数组创建一个简单的解决方案,并让它们只下载一次,但它必须在应用程序启动时加载1次。我需要以某种方式异步运行下载方法(或其他可行的解决方案)。

我如何实现这一目标?

(我正在使用故事板)

修改

enter image description here

提前致谢! 埃里克

编辑2:

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell  forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (![self.shownIndexes containsObject:indexPath]) {

    [self.shownIndexes addObject:indexPath];
    UIView *weeeeCell = [cell contentView];

    weeeeCell.layer.transform = self.initialTransform;
    weeeeCell.layer.opacity = 0.8;

    [UIView animateWithDuration:1.25 delay:0.0 usingSpringWithDamping:1.0 initialSpringVelocity:0.5 options:0 animations:^{
        weeeeCell.layer.transform = CATransform3DIdentity;
        weeeeCell.layer.opacity = 1;
    } completion:^(BOOL finished) {}];
}
}

                if ([[tableView indexPathsForVisibleRows] containsObject:indexPath]) {
                [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation: UITableViewRowAnimationAutomatic];
            }

2 个答案:

答案 0 :(得分:1)

你对这个问题的假设是正确的,你对解决方案的看法也是正确的。您提到的有关预加载图像的附加要求有点模糊。

在桌子出现之前必须加载它们吗?如果它们是异步加载的,那么您应该阻止用户访问该表,直到请求完成为止。你没有立即看到图像的糟糕体验,而是根本没有看到桌子的糟糕体验。

我认为更好的答案是懒得加载。解决方案的大纲是:

声明图像字典(由indexPaths索引)并确保将其初始化为空字典...

@interface MyViewController ()  // replace 'MyViewController' with your class
@property(strong,nonatomic) NSMutableDictionary *images;
@end

在cellForRowAtIndexPath中使用该集合......

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
    static NSString *simpleTableIdentifier = @"cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
    }

    UIImageView *cellImageView = (UIImageView *)[cell viewWithTag:40];
    UIImage *cachedImage = self.images[indexPath];
    if (cachedImage) {
        cellImageView.image = cachedImage;
    } else {
        cellImageView.image = // put a place holder image here

        // load lazily, but read on.  the code in the callback should assume
        // nothing about the state of the table when it runs

        PFFile *imageFile = [object objectForKey:@"Image"];
        [imageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
            // what if this gets run a second time before the first request finishes?
            // no worries, check for that here:
            if (!error && !self.images[indexPath]) {
                UIImage *cellImage = [UIImage imageWithData:imageData];
                self.images[indexPath] = cellImage;
                // this is important:  don't refer to cell in here, it may be
                // scrolled away and reused by the time this closure runs
                // the code we just wrote to init the cellImageView works just fine
                // call that using reload

                if ([[tableView indexPathsForVisibleRows] containsObject:indexPath]) {
                    [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
                }
            }
        }];
    }

    // Configure the cell
    UILabel *nameLabel = (UILabel*) [cell viewWithTag:10];
    nameLabel.text = [object objectForKey:@"Title"];
    nameLabel.textColor = [UIColor whiteColor];

    // Make the cell transparent
    cell.backgroundColor = [UIColor clearColor];
    cell.backgroundView = [UIView new];
    cell.selectedBackgroundView = [UIView new];

    // Resize the cell
    [cell sizeToFit];

    return cell;
}

编辑 - 现在不要为此烦恼,但是 - 如果你确实有机会在显示之前准备视图(就像这个视图控制器可能在标签栏容器中而不是默认值标签)。您可以使用表视图助手方法来预取可见行...

- (void)prepareToBeShown {
    NSArray indexPaths = [self.tableView indexPathsForVisibleRows];
    [self.tableView reloadRowsAtIndexPaths:indexPaths];    
}

编辑2:

    - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell  forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (![self.shownIndexes containsObject:indexPath]) {

        [self.shownIndexes addObject:indexPath];
        UIView *weeeeCell = [cell contentView];

        weeeeCell.layer.transform = self.initialTransform;
        weeeeCell.layer.opacity = 0.8;

        [UIView animateWithDuration:1.25 delay:0.0 usingSpringWithDamping:1.0 initialSpringVelocity:0.5 options:0 animations:^{
            weeeeCell.layer.transform = CATransform3DIdentity;
            weeeeCell.layer.opacity = 1;
        } completion:^(BOOL finished) {}];
    }
}

答案 1 :(得分:0)

您是否考虑过使用PFImageView而不是UIImageView?

所有你必须这样做设置它的文件并告诉它在后台加载。在我的tableviews中使用它们时,我从来没有任何延迟。