2015-05-29 7 views
5

wiem, że nie ma już na to pytanie na SO, ale nie sądzę, że dana odpowiedź jest satysfakcjonująca/pełna: How can IOS Photos app can show hundreds of photos in one screen?Jak korzystać PHCachingImageManager

Co chcę osiągnąć

I chcą czegoś takiego jak wybór obrazu w WhatsApp (iOS) (patrz zrzut ekranu). Po otwarciu aparatu znajduje się również poziomy suwak, w którym można zobaczyć wszystkie obrazy z galerii.

whatsapp screenshot

Co próbowałem

Teraz mam następujący kod w moim appDelegate:

let options = PHFetchOptions() 

options.sortDescriptors = [ 
    NSSortDescriptor(key: "creationDate", ascending: true) 
] 
if let results = PHAsset.fetchAssetsWithMediaType(.Image, options: options) { 

    results.enumerateObjectsUsingBlock { (object, idx, _) in 
     if let asset = object as? PHAsset { 
      Variables.assets.append(asset) 
     } 
    } 

    println(Variables.assets.count) 

    Variables.imageManager.startCachingImagesForAssets(Variables.assets, targetSize: CGSizeMake(viewWidth, viewWidth), contentMode: .AspectFill, options: self.requestOptions) 
} 

Później załadować obrazy w UITableViewController i wywołać następującą funkcję przewiń:

func fetchPhotoAtIndex(index : Int) { 

    let asset = Variables.assets[Variables.assets.count - 1 - index] as PHAsset 

    Variables.imageManager.requestImageForAsset(asset, targetSize: CGSizeMake(self.viewWidth, self.viewWidth), contentMode: .AspectFill, options: self.requestOptions, resultHandler: { (image, _) in 

     println("\(asset.localIdentifier) \(asset.creationDate) ") 

     [...] 

     self.tableView.reloadData() 
    }) 

} 

Działa to dobrze, ale mam problem z tym, że przy każdym uruchomieniu aplikacji wszystkie moje zasoby są buforowane. Czy to właściwe podejście do mojego problemu? Jak mogę wyświetlać zdjęcia galerii w czasie rzeczywistym, na przykład WhatsApp?

+0

Dlaczego chcesz skorzystać z zasobów, gdy możesz je ponownie pobrać? Przechowywanie ich może być trudne, ponieważ niektóre zasoby mogły zostać usunięte/dodane/zmodyfikowane po zabiciu aplikacji. Nie musisz też pobierać ich wszystkich ponownie. Powinieneś po prostu pobrać te, które chcesz pokazać użytkownikowi na ekranie w tym czasie. Pomogłoby to w pewnym kontekście potrzeby przechowywania. – jarora

+0

@jarora Edytowałem całe moje pytanie, pod warunkiem podania kodu i przykładu, który chcę osiągnąć. –

Odpowiedz

3

Odwołaj się do tego Photos Framework example W tym projekcie powinieneś zajrzeć do pliku AAPLAssetGridViewController.m oraz jak obsługuje on buforowanie w oparciu o pozycję przewijania.

- (void)updateCachedAssets 
     { 
      BOOL isViewVisible = [self isViewLoaded] && [[self view] window] != nil; 
      if (!isViewVisible) { return; } 

      // The preheat window is twice the height of the visible rect 
      CGRect preheatRect = self.collectionView.bounds; 
      preheatRect = CGRectInset(preheatRect, 0.0f, -0.5f * CGRectGetHeight(preheatRect)); 

      // If scrolled by a "reasonable" amount... 
      CGFloat delta = ABS(CGRectGetMidY(preheatRect) - CGRectGetMidY(self.previousPreheatRect)); 
      if (delta > CGRectGetHeight(self.collectionView.bounds)/3.0f) { 

       // Compute the assets to start caching and to stop caching. 
       NSMutableArray *addedIndexPaths = [NSMutableArray array]; 
       NSMutableArray *removedIndexPaths = [NSMutableArray array]; 

       [self computeDifferenceBetweenRect:self.previousPreheatRect andRect:preheatRect removedHandler:^(CGRect removedRect) { 
        NSArray *indexPaths = [self.collectionView aapl_indexPathsForElementsInRect:removedRect]; 
        [removedIndexPaths addObjectsFromArray:indexPaths]; 
       } addedHandler:^(CGRect addedRect) { 
        NSArray *indexPaths = [self.collectionView aapl_indexPathsForElementsInRect:addedRect]; 
        [addedIndexPaths addObjectsFromArray:indexPaths]; 
       }]; 

       NSArray *assetsToStartCaching = [self assetsAtIndexPaths:addedIndexPaths]; 
       NSArray *assetsToStopCaching = [self assetsAtIndexPaths:removedIndexPaths]; 

       [self.imageManager startCachingImagesForAssets:assetsToStartCaching 
                targetSize:AssetGridThumbnailSize 
                contentMode:PHImageContentModeAspectFill 
                 options:nil]; 
       [self.imageManager stopCachingImagesForAssets:assetsToStopCaching 
                targetSize:AssetGridThumbnailSize 
                contentMode:PHImageContentModeAspectFill 
                 options:nil]; 

       self.previousPreheatRect = preheatRect; 
      } 
     } 
     //In your case this computeDifference method changes to handle horizontal scrolling 
      - (void)computeDifferenceBetweenRect:(CGRect)oldRect andRect:(CGRect)newRect removedHandler:(void (^)(CGRect removedRect))removedHandler addedHandler:(void (^)(CGRect addedRect))addedHandler 
      { 
       if (CGRectIntersectsRect(newRect, oldRect)) { 
        CGFloat oldMaxY = CGRectGetMaxY(oldRect); 
        CGFloat oldMinY = CGRectGetMinY(oldRect); 
        CGFloat newMaxY = CGRectGetMaxY(newRect); 
        CGFloat newMinY = CGRectGetMinY(newRect); 
        if (newMaxY > oldMaxY) { 
         CGRect rectToAdd = CGRectMake(newRect.origin.x, oldMaxY, newRect.size.width, (newMaxY - oldMaxY)); 
         addedHandler(rectToAdd); 
        } 
        if (oldMinY > newMinY) { 
         CGRect rectToAdd = CGRectMake(newRect.origin.x, newMinY, newRect.size.width, (oldMinY - newMinY)); 
         addedHandler(rectToAdd); 
        } 
        if (newMaxY < oldMaxY) { 
         CGRect rectToRemove = CGRectMake(newRect.origin.x, newMaxY, newRect.size.width, (oldMaxY - newMaxY)); 
         removedHandler(rectToRemove); 
        } 
        if (oldMinY < newMinY) { 
         CGRect rectToRemove = CGRectMake(newRect.origin.x, oldMinY, newRect.size.width, (newMinY - oldMinY)); 
         removedHandler(rectToRemove); 
        } 
       } else { 
        addedHandler(newRect); 
        removedHandler(oldRect); 
       } 
      } 



- (NSArray *)assetsAtIndexPaths:(NSArray *)indexPaths 
    { 
     if (indexPaths.count == 0) { return nil; } 

     NSMutableArray *assets = [NSMutableArray arrayWithCapacity:indexPaths.count]; 
     for (NSIndexPath *indexPath in indexPaths) { 
      PHAsset *asset = self.assetsFetchResults[indexPath.item]; 
      [assets addObject:asset]; 
     } 
     return assets; 
    } 

Menedżer buforowanie obrazu nagrzewa się ze swoich aktywów żądanej wielkości, a następnie można je odzyskać z samego menedżera buforowania obrazu.

Mam nadzieję, że to pomoże.

+0

Witam, używam twojego kodu w mojej aplikacji i linii 'PHAsset * asset = self.assetsFetchResults [indexPath.item];' rzuca mi następujący wyjątek: '*** Kończenie aplikacji z powodu nieprzechwyconego wyjątku 'NSRangeException' , powód: '0x1957a880: index (0) beyond bounds (0)' 'To nie zdarza się za każdym razem, tj. wiem o tym wyjątku od HockeyApp. Jakiś pomysł, co może być przyczyną tego? – Banana

+0

Cóż, mam również podobne wyjątki, ale nie byłem w stanie ustalić przyczyny tego problemu. Jedyne obejście, jakie znam, to dodanie sprawdzenia: indexPath.item jarora