Получить последнее изображение из Photos.app?

Я видел, как другие приложения делают это там, где вы можете импортировать последнюю фотографию из приложения "Фото" для быстрого использования, но, насколько я знаю, я знаю только, как получить изображение, а не последнее (последнее). Может ли кто-нибудь показать мне, как получить последнее изображение?

Ответ 1

Этот фрагмент кода получит последнее изображение из рулона камеры (iOS 7 и ниже):

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 и выше:

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];

                                           });
                                       }];

Ответ 2

Отличный ответ от iBrad, работал почти отлично для меня. Исключением является то, что он возвращал изображения в исходной ориентации (например, вверх дном, -90 ° и т.д.).

Чтобы исправить это, я просто изменил fullResolutionImage на fullScreenImage.

Здесь:

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

Теперь это работает.

Ответ 3

Пример iBrad включает в себя фрагмент iOS8, который, по-видимому, работает, но я был смущен возвращаемым типом, который он описал. Вот фрагмент, который захватывает последнее изображение, включая параметры для требований к версии и размеру.

Следует отметить возможность запросить определенную версию (оригинал, текущий) и размер. В моем случае, когда я хочу применить возвращенное изображение к кнопке, я запрашиваю его размер и масштабирование, чтобы он соответствовал кнопке, на которую я применяю ее:

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];

                                           });
                                       }];

Ответ 4

Спасибо за ваш ответ iBrad Apps.

Просто хотел указать на предотвращение ошибок для особого случая, когда у пользователя нет изображений на его/ее фото-ролике (странный случай, который я знаю):

    // 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
    }

Ответ 5

Обратитесь к ответу Лиамом. fullScreenImage вернет масштабированное изображение, соответствующее размеру экрана вашего устройства. Для получения фактического размера изображения:

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

Указание класса класса Apple ALAssetRespresentation на fullResolutionImage:

Чтобы создать правильно повернутый объект UIImage из CGImage, вы используете imageWithCGImage: шкала: ориентация: или initWithCGImage: масштаб: ориентация:, передавая значения ориентации и масштаб.

Ответ 6

Я нашел опечатку, что мне стыдно признаться мне дольше, чем нужно было выяснить. Может быть, это спасет кого-то еще некоторое время.

Эта строка не имела двоеточия после indexSetWithIndex:

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

Ответ 7

Вот версия в Swift, которая запрашивает данные и преобразует их в UIImage, поскольку предоставленная версия каждый раз возвращает пустой UIImage

    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: String?,
             let orientation: UIImageOrientation,
             let info: [NSObject : AnyObject]?) -> Void in

             if let imageDataUnwrapped = imageData, lastImageRetrieved = UIImage(data: imageDataUnwrapped) {
                // do stuff with image

             }
        }
    }

Ответ 8

Ну, вот решение о том, как загрузить последнее изображение из галереи с помощью 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)
            }
        })
    }

}

Если вам нужна более высокая скорость, вы также можете использовать PHImageRequestOptions и установить их:

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

И так вы получите его в свой viewController (вы должны заменить GalleryManager.manager своим классом):

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

Ответ 9

Основываясь на ответе iBrad, вот быстрая и грязная версия Swift, которая работает для меня в 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
    })
}

Примечание: для этого требуется iOS 8.0+. Не забудьте связать структуру "Фотографии" и добавить "импортировать фотографии" в свой файл.

Ответ 10

Вот сочетание ответов iBrad и Javier (которые отлично поработали), но я получаю эскиз, а не изображение с полным разрешением. Некоторые другие могут найти это удобным.

- (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) {
    }];
}

Ответ 11

Xamarin.iOS версия принятого ответа (как получить последнее изображение), включая все уведомления из других ответов:

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

Ответ 12

Это очень крутой подход, но одна из проблем заключается в том, что вы должны иметь возможность создавать экземпляры PHPhotoLibrary и других классов PHPhoto во время выполнения, потому что в противном случае будут ошибки ссылок на iOS 7.XX. Просто хотелось указать на это, потому что Сейчас я сталкиваюсь с этими проблемами.

Кроме того, я считаю, что вам нужна слабая ссылка в рамке Photos, чтобы приложение запускалось на обоих устройствах с установленными iOS 8.XX и iOS 7.XX(хотя я еще не тестировал это.)

Одной из проблем, с которыми я сталкиваюсь, является создание экземпляра PHPhotoLibrary во время выполнения. У кого-нибудь есть фрагменты кода для этого?

На самом деле для приложения, над которым я работал, мне пришлось наконец написать код времени выполнения для создания экземпляра класса PHPhotoLibrary и вызова методов фреймворка PHOTOS, чтобы приложение работало как на iOS 7.x.x, так и на iOS 8.x.x. Кто-то может столкнуться с теми же проблемами, поэтому я предоставил код ниже →

// PHPhotoLibrary_class will only be non-nil on iOS 8.x.x
Class PHPhotoLibrary_class = NSClassFromString(@"PHPhotoLibrary");

if (PHPhotoLibrary_class) {

   /**
    *
    iOS 8..x. . code that has to be called dynamically at runtime and will not link on iOS 7.x.x ...

    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:title];
    } completionHandler:^(BOOL success, NSError *error) {
        if (!success) {
            NSLog(@"Error creating album: %@", error);
        }
    }];
    */

    // dynamic runtime code for code chunk listed above            
    id sharedPhotoLibrary = [PHPhotoLibrary_class performSelector:NSSelectorFromString(@"sharedPhotoLibrary")];

    SEL performChanges = NSSelectorFromString(@"performChanges:completionHandler:");

    NSMethodSignature *methodSig = [sharedPhotoLibrary methodSignatureForSelector:performChanges];

    NSInvocation* inv = [NSInvocation invocationWithMethodSignature:methodSig];
    [inv setTarget:sharedPhotoLibrary];
    [inv setSelector:performChanges];

    void(^firstBlock)() = ^void() {
        Class PHAssetCollectionChangeRequest_class = NSClassFromString(@"PHAssetCollectionChangeRequest");
        SEL creationRequestForAssetCollectionWithTitle = NSSelectorFromString(@"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) {
                   NSString *name = [group valueForProperty:ALAssetsGroupPropertyName];
                   if ([albumName isEqualToString: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.x.x 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);
   }];
}

Ответ 13

Следующий код работает с iOS7 и iOS8. Он также проверяет, есть ли изображение в фильтре. Перед выполнением кода вы должны проверить разрешение альбома:

// 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 interested 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");
    }];
}  

(Этот код является измененной версией здесь.)