Obtenir la dernière image de Photos.app?

J'ai vu d'autres applications le faire où vous pouvez importer la dernière photo de l'application Photos pour une utilisation rapide mais pour autant que je sache, je sais seulement comment get une image et non la dernière (la plus récente). Quelqu'un peut-il me montrer comment get la dernière image?

Cet extrait de code obtiendra la dernière image de la pellicule (iOS 7 et ci-dessous) :

ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; // Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos. [library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) { // Within the group enumeration block, filter to enumerate just photos. [group setAssetsFilter:[ALAssetsFilter allPhotos]]; // Chooses the photo at the last index [group enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) { // The end of the enumeration is signaled by asset == nil. if (alAsset) { ALAssetRepresentation *representation = [alAsset defaultRepresentation]; UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullScreenImage]]; // Stop the enumerations *stop = YES; *innerStop = YES; // Do something interesting with the AV asset. [self sendTweet:latestPhoto]; } }]; } failureBlock: ^(NSError *error) { // Typically you should handle an error more gracefully than this. NSLog(@"No groups"); }]; 

iOS 8 et supérieur:

 PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init]; fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]]; PHFetchResult *fetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions]; PHAsset *lastAsset = [fetchResult lastObject]; [[PHImageManager defaultManager] requestImageForAsset:lastAsset targetSize:self.photoLibraryButton.bounds.size contentMode:PHImageContentModeAspectFill options:PHImageRequestOptionsVersionCurrent resultHandler:^(UIImage *result, NSDictionary *info) { dispatch_async(dispatch_get_main_queue(), ^{ [[self photoLibraryButton] setImage:result forState:UIControlStateNormal]; }); }]; 

Grande réponse d'iBrad, a fonctionné presque parfaitement pour moi. L'exception étant qu'il returnnait les images à leur orientation d'origine (par exemple à l'envers, -90 °, etc.).

Pour résoudre ce problème, j'ai simplement changé fullResolutionImage en fullScreenImage .

Ici:

 UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullScreenImage]]; 

Cela fonctionne maintenant un régal.

L'exemple d'iBrad inclut un extrait iOS8 qui fonctionne apparemment, mais je me suis trouvé confus par le type de return qu'il a décrit. Voici un extrait qui saisit la dernière image, y compris les options de version et de taille.

À noter, la possibilité de requestr une version spécifique (originale, actuelle) et la taille. Dans mon cas, comme je souhaite appliquer l'image renvoyée à un button, je request qu'il soit dimensionné et mis à l'échelle pour correspondre au button auquel je l'applique:

 PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init]; fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]]; PHFetchResult *fetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions]; PHAsset *lastAsset = [fetchResult lastObject]; [[PHImageManager defaultManager] requestImageForAsset:lastAsset targetSize:self.photoLibraryButton.bounds.size contentMode:PHImageContentModeAspectFill options:PHImageRequestOptionsVersionCurrent resultHandler:^(UIImage *result, NSDictionary *info) { dispatch_async(dispatch_get_main_queue(), ^{ [[self photoLibraryButton] setImage:result forState:UIControlStateNormal]; }); }]; 

Merci pour votre réponse iBrad Apps.

Je voulais juste signaler une erreur de prévention pour le cas particulier où l'user n'a pas d'images sur son rouleau de photo (cas étrange que je connais):

  // Within the group enumeration block, filter to enumerate just photos. [group setAssetsFilter:[ALAssetsFilter allPhotos]]; //Check that the group has more than one picture if ([group numberOfAssets] > 0) { // Chooses the photo at the last index [group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:([group numberOfAssets] - 1)] options:0 usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) { // The end of the enumeration is signaled by asset == nil. if (alAsset) { ALAssetRepresentation *representation = [alAsset defaultRepresentation]; UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullScreenImage]]; [self.libraryButton setImage:latestPhoto forState:UIControlStateNormal]; } }]; } else { //Handle this special case } 

Reportez-vous à la réponse de Liam. fullScreenImage renvoie une image mise à l'échelle adaptée à la taille de l'écran de votre appareil. Pour get la taille réelle de l'image:

  ALAssetRepresentation *representation = [alAsset defaultRepresentation]; ALAssetOrientation orientation = [representation orientation]; UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullResolutionImage] scale:[representation scale] orientation:(UIImageOrientation)orientation]; 

Citant la reference de class ALAssetRepresentation d'Apple sur fullResolutionImage :

Pour créer un object UIImage correctement converti à partir de CGImage, vous utilisez imageWithCGImage: scale: orientation: ou initWithCGImage: scale: orientation:, en passant les valeurs d'orientation et d'échelle.

J'ai trouvé une faute de frappe que je suis gêné de m'avouer plus longtime que nécessaire. Peut-être que cela sauvera quelqu'un d'autre.

Cette ligne manquait un deux-points après indexSetWithIndex :

 [group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:[group numberOfAssets] - 1]options:0 usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) { 

Voici une version dans Swift qui request datatables et les convertit en UIImage, car la version fournie returnnait une UIImage vide à chaque fois

  let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)] let fetchResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: fetchOptions) if let lastAsset: PHAsset = fetchResult.lastObject as? PHAsset { let manager = PHImageManager.defaultManager() let imageRequestOptions = PHImageRequestOptions() manager.requestImageDataForAsset(lastAsset, options: imageRequestOptions) { (let imageData: NSData?, let dataUTI: Ssortingng?, let orientation: UIImageOrientation, let info: [NSObject : AnyObject]?) -> Void in if let imageDataUnwrapped = imageData, lastImageResortingeved = UIImage(data: imageDataUnwrapped) { // do stuff with image } } } 

Eh bien, voici une solution de comment charger la dernière image de la galerie avec les gars de Swift 3 :

 func loadLastImageThumb(completion: @escaping (UIImage) -> ()) { let imgManager = PHImageManager.default() let fetchOptions = PHFetchOptions() fetchOptions.fetchLimit = 1 fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)] let fetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions) if let last = fetchResult.lastObject { let scale = UIScreen.main.scale let size = CGSize(width: 100 * scale, height: 100 * scale) let options = PHImageRequestOptions() imgManager.requestImage(for: last, targetSize: size, contentMode: PHImageContentMode.aspectFill, options: options, resultHandler: { (image, _) in if let image = image { completion(image) } }) } } 

Si vous avez besoin de plus de vitesse, vous pouvez également utiliser PHImageRequestOptions et définir ceux-ci:

 options.deliveryMode = .fastFormat options.resizeMode = .fast 

Et voici comment vous l'obtenez dans votre viewController (vous devez replace GalleryManager.manager par votre class):

 GalleryManager.manager.loadLastImageThumb { [weak self] (image) in DispatchQueue.main.async { self?.galleryButton.setImage(image, for: .normal) } } 

Basé sur la réponse d'iBrad, voici une version rapide et sale de Swift qui fonctionne pour moi dans iOS 8.1:

 let imgManager = PHImageManager.defaultManager() var fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)] if let fetchResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: fetchOptions) { imgManager.requestImageForAsset(fetchResult.lastObject as PHAsset, targetSize: self.destinationImageView.frame.size, contentMode: PHImageContentMode.AspectFill, options: nil, resultHandler: { (image, _) in self.destinationImageView.image = image }) } 

Remarque : cela nécessite iOS 8.0+. Assurez-vous de lier le cadre Photos et append "importer des photos" dans votre file.

Voici une combinaison des réponses d'iBrad et de Javier (qui a bien fonctionné), mais j'obtiens l'actif miniature au lieu de l'image en pleine résolution. D'autres peuvent find cela pratique.

 - (void)setCameraRollImage { ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; [library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) { [group setAssetsFilter:[ALAssetsFilter allPhotos]]; if ([group numberOfAssets] > 0) { // Chooses the photo at the last index [group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:([group numberOfAssets] - 1)] options:0 usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) { // The end of the enumeration is signaled by asset == nil. if (alAsset) { UIImage *latestPhoto = [UIImage imageWithCGImage:[alAsset thumbnail]]; [self.cameraRollButton setImage:latestPhoto forState:UIControlStateNormal]; } }]; } } failureBlock: ^(NSError *error) { }]; } 

Xamarin.iOS version de la réponse acceptée (comment get la dernière image) y compris tous les avis d'autres réponses:

  private void ChooseLastTakenPictureImplementation() { var library = new ALAssetsLibrary(); // Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos. library.Enumerate(ALAssetsGroupType.SavedPhotos, (ALAssetsGroup assetsGroup, ref bool stop) => { if (stop || assetsGroup == null) { return; } //Xamarin does not support ref parameters in nested lamba expressions var lambdaStop = false; //Check that the group has more than one picture if (assetsGroup.Count > 0) { // Within the group enumeration block, filter to enumerate just photos. assetsGroup.SetAssetsFilter(ALAssetsFilter.AllPhotos); // Chooses the photo at the last index assetsGroup.Enumerate(NSEnumerationOptions.Reverse, (ALAsset result, int index, ref bool innerStop) => { // The end of the enumeration is signaled by asset == nil. if (result != null) { var representation = result.DefaultRepresentation; var latestPhoto = new UIImage(representation.GetImage(), representation.Scale, (UIImageOrientation)representation.Orientation); // Stop the enumerations lambdaStop = innerStop = true; // Do something interesting with the AV asset. HandleImageAutoPick(latestPhoto); } }); stop = lambdaStop; return; } else { //Handle this special case where user has no pictures } }, error => { // Typically you should handle an error more gracefully than this. Debug.WriteLine(error.Description); }); } 

C'est une approche très cool mais l'un des problèmes est que vous devez être capable d'instancier PHPhotoLibrary et les autres classs PHPhoto à l'exécution car sinon il y aura des erreurs de lien sur iOS 7.XX Je voulais juste le signaler parce que je cours dans ces questions maintenant.

Aussi, je crois que vous avez un faible lien dans le cadre Photos afin que l'application fonctionne sur les deux appareils avec iOS 8.XX et iOS 7.XX installé (même si je n'ai pas encore testé cela).

L'un des problèmes que je rencontre est de savoir comment instancier la PHPhotoLibrary à l'exécution. Est-ce que quelqu'un a des extraits de code pour cela?

En fait, pour l'application sur laquelle je travaillais, j'ai finalement dû écrire du code d'exécution pour instancier la class PHPhotoLibrary et appeler les methods du framework PHotos afin que l'application fonctionne sur iOS 7.xx et iOS 8.xx Quelqu'un d'autre peut rencontrer le même questions donc j'ai fourni le code ci-dessous ->

 // PHPhotoLibrary_class will only be non-nil on iOS 8.xx Class PHPhotoLibrary_class = NSClassFromSsortingng(@"PHPhotoLibrary"); if (PHPhotoLibrary_class) { /** * iOS 8..x. . code that has to be called dynamically at runtime and will not link on iOS 7.xx ... [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:title]; } completionHandler:^(BOOL success, NSError *error) { if (!success) { NSLog(@"Error creating album: %@", error); } }]; */ // dynamic runtime code for code chunk listd above id sharedPhotoLibrary = [PHPhotoLibrary_class performSelector:NSSelectorFromSsortingng(@"sharedPhotoLibrary")]; SEL performChanges = NSSelectorFromSsortingng(@"performChanges:completionHandler:"); NSMethodSignature *methodSig = [sharedPhotoLibrary methodSignatureForSelector:performChanges]; NSInvocation* inv = [NSInvocation invocationWithMethodSignature:methodSig]; [inv setTarget:sharedPhotoLibrary]; [inv setSelector:performChanges]; void(^firstBlock)() = ^void() { Class PHAssetCollectionChangeRequest_class = NSClassFromSsortingng(@"PHAssetCollectionChangeRequest"); SEL creationRequestForAssetCollectionWithTitle = NSSelectorFromSsortingng(@"creationRequestForAssetCollectionWithTitle:"); [PHAssetCollectionChangeRequest_class performSelector:creationRequestForAssetCollectionWithTitle withObject:albumName]; }; void (^secondBlock)(BOOL success, NSError *error) = ^void(BOOL success, NSError *error) { if (success) { [assetsLib enumerateGroupsWithTypes:ALAssetsGroupAlbum usingBlock:^(ALAssetsGroup *group, BOOL *stop) { if (group) { NSSsortingng *name = [group valueForProperty:ALAssetsGroupPropertyName]; if ([albumName isEqualToSsortingng:name]) { groupFound = true; handler(group, nil); } } } failureBlock:^(NSError *error) { handler(nil, error); }]; } if (error) { NSLog(@"Error creating album: %@", error); handler(nil, error); } }; // Set the first and second blocks. [inv setArgument:&firstBlock atIndex:2]; [inv setArgument:&secondBlock atIndex:3]; [inv invoke]; } else { // code that always creates an album on iOS 7.xx but fails // in certain situations such as if album has been deleted // previously on iOS 8...x. . [assetsLib addAssetsGroupAlbumWithName:albumName resultBlock:^(ALAssetsGroup *group) { handler(group, nil); } failureBlock:^(NSError *error) { NSLog( @"Failed to create album: %@", albumName); handler(nil, error); }]; } 

Le code suivant fonctionne avec iOS7 et iOS8. Il vérifie également s'il y a une image dans le filter. Avant d'exécuter le code, vous devez vérifier l'autorisation de l'album:

 // get the latest image from the album -(void)getLatestPhoto { NSLog(@"MMM TGCameraViewController - getLatestPhoto"); ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; // Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos. [library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) { // Within the group enumeration block, filter to enumerate just photos. [group setAssetsFilter:[ALAssetsFilter allPhotos]]; // For this example, we're only interestd in the last item [group numberOfAssets]-1 = last. if ([group numberOfAssets] > 0) { [group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:[group numberOfAssets]-1] options:0 usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) { // The end of the enumeration is signaled by asset == nil. if (alAsset) { ALAssetRepresentation *representation = [alAsset defaultRepresentation]; // Do something interesting with the AV asset. UIImage *img = [UIImage imageWithCGImage:[representation fullScreenImage]]; // use the photo here ... // we only need the first (most recent) photo -- stop the enumeration *innerStop = YES; } }]; } } failureBlock: ^(NSError *error) { // Typically you should handle an error more gracefully than this. NSLog(@"No groups"); }]; } 

(Ce code est une version modifiée d' ici .)