NSCollectionView stück für stück füllen

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • NSCollectionView stück für stück füllen

    hallo zusammen,

    ich habe einen NSCollectionView und einen sack voll bilder.
    der NSCollectionView bekommt seine Daten aus einem arraycontroller.

    ich erstelle pro bild eine datenklasse die erstmal nur den pfad und den namen als bild bekommt. (in einem background thread)
    die datenklasse sorgt selbst für das laden und ggf erstellen eines thumbnails vom Bild. (per nsoperation und nsoperationqueue)

    das funktioniert auch alles wunderbar, nur es gefällt mir nicht.
    der collectionview zeigt erst etwas an, wenn alle bilder geladen sind.
    ich hätte gern, dass erstmal alle bilder mit dummybild (wird beim erstellen der datenklasse geladen) angezeigt werden und dann nach und nach
    die ganzen bilder geladen werden. ich hoffe ich hab mich halbwegs verständlich ausgedrückt.

    hier noch die controllerklasse und die datenklasse


    Quellcode

    1. #import "ImageBrowserController.h"
    2. #import "VIPImage.h"
    3. @implementation ImageBrowserController
    4. @synthesize xibHasLoaded, imagesArray, operationQueue;
    5. SYNTHESIZE_SINGLETON_FOR_CLASS(ImageBrowserController);
    6. #pragma mark - Private Methods
    7. - (void) addImage : (NSDictionary*) dict {
    8. VIPImage *i = [VIPImage vipImageWithDict:dict andDelegate:self];
    9. [self.imageArrayController addObject:i];
    10. }
    11. - (void) loadImagesInBackground {
    12. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    13. //int i = 0;
    14. for (NSString *file in [fileManager contentsOfDirectoryAtPath:@"/Volumes/1TB/Picts" error:nil]) {
    15. //if (i > 100) break;
    16. //i++;
    17. if (![[[file pathExtension] lowercaseString] isEqualToString:@"jpg"]) continue;
    18. [self performSelectorOnMainThread:@selector(addImage:) withObject:[NSDictionary dictionaryWithObjectsAndKeys:file, @"imageName", @"/Volumes/1TB/Picts", @"imagePath", nil] waitUntilDone:NO];
    19. //[self addImage:[NSDictionary dictionaryWithObjectsAndKeys:file, @"imageName", @"/Volumes/1TB/Picts", @"imagePath", nil]];
    20. }
    21. [pool drain];
    22. }
    23. #pragma mark - Public Methods
    24. - (void) initImageBrowser {
    25. if (!self.xibHasLoaded)
    26. [NSBundle loadNibNamed:@"ImageBrowser" owner:self];
    27. [self.imageBrowserWindow makeKeyAndOrderFront:nil];
    28. }
    29. #pragma mark - Initialization
    30. - (id)init {
    31. self = [super init];
    32. if (self) {
    33. self.xibHasLoaded = NO;
    34. self.imagesArray = [NSMutableArray array];
    35. self.operationQueue = [[[NSOperationQueue alloc] init] autorelease];
    36. }
    37. return self;
    38. }
    39. - (void)dealloc {
    40. self.imagesArray = nil;
    41. [super dealloc];
    42. }
    43. - (IBAction)update:(id)sender {
    44. [NSThread detachNewThreadSelector:@selector(loadImagesInBackground) toTarget:self withObject:nil];
    45. }
    Alles anzeigen



    Quellcode

    1. @implementation VIPImage
    2. #pragma mark - Initialization
    3. - (id)init {
    4. self = [super init];
    5. if (self) {
    6. self.thumbImage = [NSImage imageNamed:@"dummyImage"];
    7. self.thumbImagePath = @"/Users/sascha/Desktop/thumbImages";
    8. self.needSave = NO;
    9. self.observersHooked = NO;
    10. }
    11. return self;
    12. }
    13. - (void)dealloc {
    14. [self removeObservers];
    15. self.delegate = nil;
    16. self.thumbImage = nil;
    17. self.thumbImagePath = nil;
    18. self.imageName = nil;
    19. self.imagePath = nil;
    20. [super dealloc];
    21. }
    22. + (VIPImage*) vipImage {
    23. return [[[VIPImage alloc] init] autorelease];
    24. }
    25. + (VIPImage*) vipImageWithDict: (NSDictionary*) dict andDelegate: (ImageBrowserController*) anDelegate {
    26. VIPImage *vipImage = [VIPImage vipImage];
    27. vipImage.imageName = ([dict objectForKey:@"imageName"] == [NSNull null]) ? @"" : [dict objectForKey:@"imageName"];
    28. vipImage.imagePath = ([dict objectForKey:@"imagePath"] == [NSNull null]) ? @"" : [dict objectForKey:@"imagePath"];
    29. vipImage.delegate = anDelegate;
    30. if (vipImage.delegate) {
    31. VIPImageThumbOperation *op = [[[VIPImageThumbOperation alloc] initWithVIPImage:vipImage] autorelease];
    32. [anDelegate.operationQueue addOperation:op];
    33. }
    34. return vipImage;
    35. }
    36. + (VIPImage*) vipImageWithDict: (NSDictionary*) dict {
    37. return [VIPImage vipImageWithDict:dict andDelegate:nil];
    38. }
    39. @end
    Alles anzeigen
  • macuser schrieb:


    der collectionview zeigt erst etwas an, wenn alle bilder geladen sind.
    ich hätte gern, dass erstmal alle bilder mit dummybild (wird beim erstellen der datenklasse geladen) angezeigt werden und dann nach und nach

    Ja, klingt gut. Dann mache es einfach so wie Du schreibst. Dummybild anzeigen und ersetzen sobald das Laden fertig ist.

    Was war nochmal die Frage?
  • Ich habe das mal so gemacht:

    Meine Image-Klasse hat eine Property:
    -(UIImage)thumbnail{
    if (!_thumbnail) {
    [self loadThumbnailImage]; // Hier meine Laderoutine, ich lade die aus dem Netz, bei dir ein 'PerformInBackground.. ' starten
    } else {

    return _thumbnail;
    }

    -(void)loadThumbnailImage{
    .. Laden ausführen
    .. entweder:

    [self willChangeValueForKey:@"Thumbnail"]; // KVO benachrichtigen
    // .. Bild Laden
    _thumbnail = ...
    [self didChangeValueForKey:@"Thumbnail"];

    // Damit sollte das CollectionView alle Thumbnails deiner Klasse der reihe nach anfordern und im Hintergrund laden

    }
  • Das sollte folgendermaßen klappen:

    über alle Bilder Deiner Datasource iterieren, dann jeweils im Hintergrund laden und dann von dort aus den image-Setter der ImageEntity im MainThread aufrufen:

    Quellcode

    1. ImageEntity (wird vom CollectionView dargestellt und Deinem NSArrayController verwaltet)
    2. ->property image
    3. ->property path;
    4. // in der Datasource
    5. - (void)updateImages
    6. {
    7. for ( ImageEntity *entity in self.entities ) {
    8. if ( entity.image == nil ) {
    9. [ self.operationQueue addOperationWithBlock:^{
    10. NSImage *image = ... image laden
    11. [ [ NSOperationQueue mainQueue ] addOperationWithBlock:^{
    12. entity.image = image;
    13. } ];
    14. } ];
    15. }
    16. }
    17. }
    Alles anzeigen

    Im NSImageView im collectionview setzt Du einen custom-NSValueTransformer welcher für ein nil-value einfach ein Platzhalterbild zurückgibt (bzw. Du schreibst Dir einen custom getter von ImageEntitys image-property und gibst da das Platzhalterbild zurück, falls die ivar noch nil ist).

    Gruß, Markus
  • danke für eure Hilfe. ich habe jetzt herausgefunden, dass es nicht am code liegt.
    das problem liegt beim nscollectionview. denn dieser läd aus mir unverständlichen gründen
    erstmal alle Bilder bis unten durch und nicht nur den sichbaren Bereich. aus diesem Grund
    ist das teil so schrecklich langsam.

    ich benutze jetzt einen IKImageBrowserView der mit meinem code genau das macht was er soll.
    und das auch noch richtig schnell und mit eingebautem zoom.
    einziger nachteil, ich bin nicht so flexibel bei der Darstellung.