2013-05-25 20 views
7

私はちょうどUICollectionViewを作成しました。ユーザーは自分の携帯電話から画像をアプリのフォトアルバム機能に追加できます。イメージをドキュメントディレクトリのサブディレクトリに保存しておくと、さらに多くのイメージを追加したり削除したりすることができます。しかし、コレクションビューを上下にスクロールすると、非常に遅いです。UICollectionViewのスクロールが遅い

スクロールを素早く滑らかにするにはどうすればよいですか?

マイコード:最初の16枚の画像は、ドキュメントディレクトリ内のサブディレクトリからであることの後に、プリセット画像すべて

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
    CollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Custom" forIndexPath:indexPath]; 
    //Current index number 
    int index=indexPath.section * noOfSection + indexPath.row; 
    //Check if its the preset photos 
    if(index<16){ 
     NSString *name=[recipePhotos objectAtIndex:indexPath.section * noOfSection + indexPath.row]; 
     cell.imageView.image=[UIImage imageNamed:name]; 
    } 

//not preset photos, so retrieve the photos the user added 
    else { 
     NSData *data= [NSData dataWithContentsOfFile:[recipePhotos objectAtIndex:index]]; 
     UIImage *theImage=[UIImage imageWithData:data]; 

     cell.imageView.image=theImage; 
     data=nil; 
    } 

    return cell; 
} 

時間プロファイラが私に与えたです。この

Running Time Self  Symbol Name 
568.0ms 63.1% 0.0  Main Thread 0x4048 
320.0ms 35.5% 0.0  _pthread_start 0x405e 
320.0ms 35.5% 0.0  thread_start 
320.0ms 35.5% 0.0  _pthread_start 
320.0ms 35.5% 0.0  0x1084be960 
310.0ms 34.4% 1.0   0x1084be6f0 
7.0ms 0.7% 0.0   mach_msg 
2.0ms 0.2% 2.0   objc_msgSend 
1.0ms 0.1% 1.0   -[NSAutoreleasePool release] 
4.0ms 0.4% 0.0  _dispatch_mgr_thread 0x4052 
4.0ms 0.4% 0.0  _dispatch_mgr_thread 
4.0ms 0.4% 0.0  _dispatch_mgr_invoke 
4.0ms 0.4% 4.0  kevent 
3.0ms 0.3% 0.0  _dispatch_worker_thread2 0x62b24 
3.0ms 0.3% 1.0  start_wqthread 
3.0ms 0.3% 0.0  _dispatch_worker_thread2 0x62a84 
3.0ms 0.3% 0.0  start_wqthread 
3.0ms 0.3% 0.0  _pthread_wqthread 
3.0ms 0.3% 0.0  _dispatch_worker_thread2 
3.0ms 0.3% 0.0   _dispatch_queue_invoke 
3.0ms 0.3% 0.0   _dispatch_queue_drain 
3.0ms 0.3% 0.0   _dispatch_client_callout 
2.0ms 0.2% 0.0   my_io_execute_passive_block 
1.0ms 0.1% 0.0    __86-[NSPersistentUIManager writePublicPlistWithOpenWindowIDs:optionallyWaitingUntilDone:]_block_invoke_0835 
1.0ms 0.1% 0.0    -[NSPersistentUIManager writePublicPlistData:] 
1.0ms 0.1% 0.0    -[NSURL(NSURLPathUtilities) URLByAppendingPathComponent:] 
1.0ms 0.1% 0.0    -[NSURL getResourceValue:forKey:error:] 
1.0ms 0.1% 0.0     CFURLCopyResourcePropertyForKey 
1.0ms 0.1% 0.0    __block_global_2 
1.0ms 0.1% 0.0    -[NSPersistentUIManager writeRecords:withWindowInfos:flushingStaleData:] 
1.0ms 0.1% 0.0   _dispatch_call_block_and_release 
1.0ms 0.1% 0.0    0x1084b8580 
1.0ms 0.1% 0.0    mach_msg_send 
1.0ms 0.1% 0.0    mach_msg 
1.0ms 0.1% 1.0    mach_msg_trap 
1.0ms 0.1% 0.0  _pthread_struct_init 0x62a83 
1.0ms 0.1% 0.0  start_wqthread 
1.0ms 0.1% 0.0  _pthread_wqthread 
1.0ms 0.1% 1.0  _pthread_struct_init 
1.0ms 0.1% 0.0  start_wqthread 0x62a7f 
+1

あなたはそうのようにそれを使用することができますか?私の経験から、あまりにも多くの画像がフレームレートを遅くする傾向があります。 – LAMBORGHINI

+1

ディレクトリからの画像を読み込んでいるセルでのみ発生します..上のコードをチェックしてください。私はちょうどそれを更新しました@CodeBandits –

+0

'CollectionCell'のカスタム図面はありますか? – rog

答えて

2

だから私はいくつか混乱した後、問題はいくつかの要因に基づいていることを理解しました。

ワンサムネイルの画像が大きかったので、私がしたことは、セルに収まるような画像サイズの小さい別々の画像の配列になりました。 2つ - @ggranaの助けを借りて、別々のスレッドを開くとプロセスがスピードアップし、遅くなることはありませんでした。 3つ - 私は、画像の位置ではなく画像の配列を持つ方が高速であることを発見しました。

+0

多くの画像がない場合は、平均的なユーザーがコレクションビュー全体をスクロールすると、最後に同じメモリを使用して終了します。画像の配列が速ければ、そのようになります。 –

+0

あなたはこれが必要ですhttp://stackoverflow.com/a/26034382/294884これはhttp://stackoverflow.com/a/25604378/294884 – Fattie

3

あなたが行う必要がありますテーブルビューで行う必要があるようなアプローチでは、テーブルビューでセルを再利用するなど、ビューを再利用する必要があります。

A本当に良いチュートリアルでは、レイWenderlichから、このいずれかになります。

:あなたは基本的には、彼らは再利用可能なビューについて話秒1で、あなたは、リンクを見てみましょう持っている最初の部分では

http://www.raywenderlich.com/22417/beginning-uicollectionview-in-ios-6-part-22

編集

画像の非同期をロードする例:

は、あなたがそれをこのようにCALするパスを受けたこと、例えば、あなたのセルに方法loadImageFromFileを作成します。

[cell loadImageFromFile:[recipePhotos objectAtIndex:index]]; 

そして(多分あなたは何かを適応させる必要があります...)のようになります。

- (void) loadImageFromFile:(NSString*)path{ 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{ 
     NSData *data= [NSData dataWithContentsOfFile:path]; 
     UIImage *theImage=[UIImage imageWithData:data]; 

     dispatch_async(dispatch_get_main_queue(), ^{ 
      cell.imageView.image=theImage; 
    }); 
}); 
+0

再利用可能なセルを使用しています... @ggranaの上にコードを挿入します –

+0

イメージをバックグラウンドで読み込む必要があります。読み込まれたら、セル内のイメージを更新する必要があります。スロー。 – ggrana

+0

さて、バックグラウンドで世話をするのに最適なアプローチは何ですか?私はメインスレッドから分離する必要がありますか?もしそうなら@ggrana –

3

@ggranaは正しいアイデアを持っています。非同期の読み込みは間違いなく助けになります。しかし、毎回ファイルからロードすると、あなたはまだ冗長な作業をしています。考慮する1つの点は、非同期ロードをNSCacheで補強することです。基本的にはNSDictionaryですが、メモリ管理自体を行い、メモリが不足している場合はデータをダンプします。

メモリの予算がある場合は、サムネイルを実際に作成して(サイズをハードコードする必要がないため)、キャッシュに保存することができます。そうすれば、あなたの画像は最初にポップするだけです。その後、毎回ロードされます。ビューにあるどのように多くの画像

@implementation ... 
{ 
    NSCache * _johnny; // get it? 
} 

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
    [cell setImage:nil]; // since they are reused, prevents showing an old image 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
     UIImage * sticker = [self thumbnailOfSize:CGSizeMake(desiredImageWidth, desiredImageHeight) 
              forIndex:[indexPath row]]; 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      [cell setImage:sticker]; 
     }); 
    }); 
} 

// load image from disk and cache thumbnail version 
- (UIImage*) thumbnailOfSize:(CGSize)size forIndex:(NSInteger)index 
{ 
    NSString * cacheKey = [NSString stringWithFormat:@"%@ %d", NSStringFromCGSize(size), index]; 
    UIImage * image = [_johnny objectForKey:cacheKey]; 

    if (!image) { 
     image = [UIImage imageWithContentsOfFile:_imagePaths[index]]; 

     float desiredWidth = size.width; 
     float desiredHeight = size.height; 
     float actualHeight = image.size.height; 
     float actualWidth = image.size.width; 
     float imgRatio = actualWidth/actualHeight; 
     float maxRatio = desiredWidth/desiredHeight; 

     if(imgRatio != maxRatio) { 
      if(imgRatio < maxRatio) { 
       imgRatio = desiredHeight/actualHeight; 
       actualWidth = imgRatio * actualWidth; 
       actualHeight = desiredHeight; 
      } else { 
       imgRatio = desiredWidth/actualWidth; 
       actualHeight = imgRatio * actualHeight; 
       actualWidth = desiredWidth; 
      } 
     } 

     CGRect rect = CGRectMake(0.0, 0.0, actualWidth, actualHeight); 
     UIGraphicsBeginImageContextWithOptions(rect.size, FALSE, 0); // do right thing for retina 
     [image drawInRect:rect]; 
     image = UIGraphicsGetImageFromCurrentImageContext(); 
     UIGraphicsEndImageContext(); 

     // here's johnny 
     [_johnny setObject:image forKey:cacheKey]; 
    } 

    return image; 
} 
+0

このアプローチはうまくいきます。いくつかの点:1) 'cellForItem ... 'のセル作成/デキュー行が抜けています2)ユーザーが長いコレクションビューの一番下にスクロールすると、一番下のセルが循環表示されますその再使用されたセルに対する一連の非同期呼び出しとしての一連の画像が最終的に画像を更新する。 'indexPathsForVisibleCells 'の最後のチェックでこれが解決されます(基本的にはこのインデックスパスはまだ表示されていますか?そうでない場合は、イメージを更新しないでください)。 –

関連する問題