2014-10-17 27 views
0

我在我的IOS 8 Objective-c iPhone應用程序中使用Parse分析PFQueryTableViewController。UITableView和解析 - laggy

我的列表包含一個標籤和一個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; 
} 

此代碼的工作不錯,但滾動有點laggy,我認爲這是因爲它的下載圖像時顯示的細胞。我想通過在本地創建一個圖像數組創建一個簡單的解決方案,並讓他們只下載一次,但它必須在應用程序啓動時加載最少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]; 
      } 

回答

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) {}]; 
    } 
} 
+0

我想我明白你的觀點,但我不完全理解其含義以及如何實現你的代碼。你能否詳細說明一下? – Erik 2014-10-18 16:48:34

+0

@Erik - 查看編輯。將代碼建議放在上下文中。只需將實例變量和改進的cellForRowAtIndexPath粘貼到您的視圖控制器中即可。 – danh 2014-10-19 01:29:30

+0

偉大的答案 - 徹底,簡潔的解釋 – 2014-10-19 01:39:39

0

你有沒有想過使用PFImageView,而不是一個UIImageView的?

你所要做的就是設置它的文件並告訴它在後臺加載。在我的桌面視圖中使用它們時我從來沒有任何滯後。

+0

這不就是我的代碼在做什麼?我使用getDataInBackgroundWithBlock方法,所以如果我使用PFImageView,我應該很好? – Erik 2014-10-17 22:54:59

+0

您可能也可以嘗試使用fetchIfNeededInBackgroundWithBlock。 – hybrdthry911 2014-10-17 23:14:44

+0

哪裏?請您詳細說明:) – Erik 2014-10-18 16:50:36