Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to generate thumbnails of images on iCloud Drive?

This appears easy, but the lack of documentation makes this question impossible to guess.

I have pictures and videos on my app's icloud drive and I want to create thumbnails of these assets. I am talking about assets on iCloud Drive, not the iCloud photo stream inside the camera roll. I am talking about the real iCloud Drive folder.

Creating thumbnails from videos are "easy" compared to images. You just need 2 weeks to figure out how it works, having in mind the poor documentation Apple wrote but thumbnails from images seem impossible.

What I have now is an array of NSMetadataItems each one describing one item on the iCloud folder.

These are the methods I have tried so far that don't work:


METHOD 1

[fileURL startAccessingSecurityScopedResource];

NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] init];
__block NSError *error;

[coordinator coordinateReadingItemAtURL:fileURL
                                options:NSFileCoordinatorReadingImmediatelyAvailableMetadataOnly
                                  error:&error
                             byAccessor:^(NSURL *newURL) {
                               NSDictionary *thumb;
                               BOOL success = [newURL getResourceValue:&thumb forKey:NSURLThumbnailDictionaryKey error:&error];

                               UIImage *thumbnail = thumb[NSThumbnail1024x1024SizeKey];



                             }];

[fileURL stopAccessingSecurityScopedResource];

The results of this method are fantastic. Ready for that? Here we go: success = YES, error = nil and thumbnail = nil.


ANOTHER METHOD

AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:fileURL
                                            options:nil];

AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:asset];

imageGenerator.appliesPreferredTrackTransform = YES;

CMTime time = CMTimeMake(0, 60); // time range in which you want
NSValue *timeValue = [NSValue valueWithCMTime:time];

[imageGenerator generateCGImagesAsynchronouslyForTimes:@[timeValue] completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * error) {

  thumbnail = [[UIImage alloc] initWithCGImage:image];

}];

error = The requested URL was not found on this server. and thumbnail = nil

This method appears to be just for videos. I was trying this just in case. Any equivalent of this method to images?


PRIMITIVE METHOD

NSData *tempData = [NSData dataWithContentsOfUrl:tempURL];

NOPE - data = nil


METHOD 4

The fourth possible method would be using ALAsset but this was deprecated on iOS 9.

I think that all these methods fail because they just work (bug or not) if the resource is local. Any ideas on how to download the image so I can get the thumbnail?

Any other ideas?

thanks


EDIT: after several tests I see that Method 1 is the only one that seems to be in the right direction. This method works poorly, sometimes grabbing the icon but most part of the time not working.


Another point is this. Whatever people suggests me, they always say about downloading the whole image to get the thumbnail. I don't think this is the way to go. Just see how getting thumbnails of video work. You don't download the whole video to get its thumbnail.

So this question remains open.

like image 637
Duck Avatar asked Sep 10 '15 02:09

Duck


People also ask

How does iCloud photo optimization work?

With Optimize Storage, smaller, space-saving photos and videos are kept on your device while all of your original, full-resolution versions are stored in iCloud. And as long as you have enough space in iCloud, you can store as many photos and videos as you want.


2 Answers

The Photos-Framework or AssetsLibrary will not work here as you would have to import your iCloud Drive Photos first to the PhotoLibrary to use any methods of these two frameworks.

What you should look at is ImageIO: Get the content of the iCloud Drive Photo as NSData and then proceed like this:

CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)(imageData), NULL );

NSDictionary* thumbOpts = [NSDictionary dictionaryWithObjectsAndKeys:
                                    (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform,
                                    (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageAlways,
                                    [NSNumber numberWithInt:160],(id)kCGImageSourceThumbnailMaxPixelSize,
                                    nil];

CGImageRef  thumbImageRef = CGImageSourceCreateThumbnailAtIndex(source,0,(__bridge CFDictionaryRef)thumbOpts);

UIImage *thumbnail = [[UIImage alloc] initWithCGImage: thumbImageRef];
like image 157
holtmann Avatar answered Oct 20 '22 01:10

holtmann


After testing several solutions, the one that seems to work better is this one:

[fileURL startAccessingSecurityScopedResource];

NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] init];
__block NSError *error;

[coordinator coordinateReadingItemAtURL:fileURL
                                options:NSFileCoordinatorReadingImmediatelyAvailableMetadataOnly
                                  error:&error
                             byAccessor:^(NSURL *newURL) {
                               NSDictionary *thumb;
                               BOOL success = [newURL getResourceValue:&thumb forKey:NSURLThumbnailDictionaryKey error:&error];

                               UIImage *thumbnail = thumb[NSThumbnail1024x1024SizeKey];



                             }];

[fileURL stopAccessingSecurityScopedResource];

This solution is not perfect. It will fail to bring the thumbnails sometimes but I was not able to find any other solution that works 100%. Others are worst than that.

like image 45
Duck Avatar answered Oct 19 '22 23:10

Duck