Я видел, как другие приложения делают это там, где вы можете импортировать последнюю фотографию из приложения "Фото" для быстрого использования, но, насколько я знаю, я знаю только, как получить изображение, а не последнее (последнее). Может ли кто-нибудь показать мне, как получить последнее изображение?
Получить последнее изображение из 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");
}];
}
(Этот код является измененной версией здесь.)