2013-05-25 16 views
7

Ich habe gerade eine UICollectionView erstellt, in der der Benutzer Bilder von ihrem Handy zur Fotoalbum-Funktion in der App hinzufügen können. Ich habe die Bilder in einem Unterverzeichnis im Dokumentenverzeichnis gespeichert, so dass mehr hinzugefügt und entfernt werden kann. Wenn ich jedoch die Sammlungsansicht auf und ab blättere, ist es sehr lückig.UICollectionView Scrolling ist langsam

Wie kann ich die Rolle schön und glatt machen?

Mein Code: Die ersten 16 Bilder sind voreingestellt alles Bilder nach, die von einem Unterverzeichnis sind in den Dokumenten Verzeichnis

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

Zeit Profiler gab mir diese

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

, wie viele Bilder sind in der Ansicht? Aus meiner Erfahrung können zu viele Bilder dazu neigen, die Bildrate zu verlangsamen. – LAMBORGHINI

+1

Es passiert nur mit den Zellen geladen Bilder aus dem Verzeichnis .. überprüfen Sie oben meinen Code .. ich habe es gerade aktualisiert @CodeBandits –

+0

Machst du eine benutzerdefinierte Zeichnung in 'CollectionCell'? – rog

Antwort

2

Also Nach ein paar Herumspielen, habe ich herausgefunden, dass das Problem auf ein paar Faktoren beruhte.

Eins - Die Bilder für die Vorschaubilder waren zu groß, also habe ich eine separate Reihe von Bildern mit kleineren Bildgrößen gemacht, die zur Zelle passen würden. Zwei- Mit der Hilfe von @ggrana beschleunigte das Öffnen eines separaten Threads den Prozess und machte ihn weniger lückig. Drei - Ich fand auch, dass ein Array von Bildern anstelle von Bildstandorten schneller war - nur ein Problem ist, dass es mehr Speicher beansprucht.

+0

Wenn Sie nicht viele Bilder haben, meine ich wenn Der durchschnittliche Benutzer scrollt alle durch Ihre Sammlung Ansicht, sie am Ende mit dem gleichen Speicher am Ende. Also, wenn die Anordnung der Bilder schneller ist, gehen Sie so. –

+0

benötigen Sie diese http://stackoverflow.com/a/26034382/294884 und diese http://stackoverflow.com/a/25604378/294884 – Fattie

3

Sie machen müssen Ein Ansatz wie der, den Sie in den Tabellenansichten ausführen müssen, Sie müssen die Ansichten wiederverwenden, so wie Sie Ihre Zellen in der Tabellenansicht wiederverwenden.

Ein wirklich gutes Tutorial ist dies eine von Ray Wenderlich:

Im ersten Teil Sie den Grund haben, in der zweiten, den sie über die wieder verwendbaren Ansichten zu sprechen, nehmen Sie einen Blick auf dem Link:

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

bearbeiten

Beispielbilder async zu laden:

Erstellen Sie Ihr Handy ein Verfahren loadImageFromFile zum Beispiel, dass der Weg erhält man es auf diese Weise cal wird:

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

Und dann aussehen wird (vielleicht brauchen Sie etwas anpassen ...):

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

Ich benutze wiederverwendbare Zellen ... Heres meinen Code über @ggrana –

+0

Sie müssen das Bild im Hintergrund laden, und wenn Sie geladen werden müssen Sie das Bild in der Zelle aktualisieren, andernfalls wird Ihre Bildlauf warten das Bild zurück, um fortzufahren langsam. – ggrana

+0

Okay, was ist der beste Ansatz, um es im Hintergrund zu kümmern. Würde ich mich vom Hauptthread trennen müssen? wenn ja, wie @ggrana –

3

@ggrana hat die richtige Idee. Das Laden von Async wird definitiv helfen. Sie arbeiten jedoch immer noch redundant, wenn Sie jedes Mal aus der Datei laden. Eine Sache zu berücksichtigen wäre, die Async-Lade mit einem NSCache zu erhöhen. Es ist im Grunde ein NSDictionary, aber führt Speicherverwaltung selbst aus und gibt Daten aus, wenn Speicherdruck vorhanden ist.

Wenn Sie also über das Speicherbudget verfügen, können Sie Ihre Thumbnails im Handumdrehen erstellen (Sie müssen also keine Größe fest codieren) und diese im Cache speichern. Auf diese Weise erscheinen Ihre Bilder nur beim ersten Mal. Jedes Mal danach laden sie sofort.

Sie können es wie so verwenden:

@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

Dieser Ansatz funktioniert gut. Ein paar Punkte: 1) Sie fehlen die Zeile zum Erstellen/Entfernen von Zellen in 'cellForItem ...' 2) Wenn ein Benutzer sehr schnell zum Ende einer langen Sammlungsansicht scrollt, scheinen die unteren Zellen scheinbar durchzulaufen Eine Reihe von Bildern als ein Bündel von Async-Aufrufen für diese wiederverwendete Zelle aktualisieren schließlich das Bild. Eine Überprüfung in letzter Minute von 'indexPathsForVisibleCells' behebt dies (im Grunde "ist dieser Indexpfad noch sichtbar? Wenn nicht, aktualisieren Sie das Bild nicht). –