I would like to get some photos from the Asset Library using the url of the asset as filter parameter:
ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init];
[assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos
usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if (nil != group) {
// be sure to filter the group so you only get photos
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
[group enumerateAssetsUsingBlock:
^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (nil != result) {
ALAssetRepresentation *repr = [result defaultRepresentation];
BOOL exists = [...[repr.url absoluteString]];
....
The problem is that the enumerateAssetsUsingBlock block get all the photos one by one and that process is too slow. Is there any other way to filter the asset group?
Thanks
Related
We have about 2k objects, which are instance of class ALAsset, and we need to know, which files are panoramic images.
We have tried to get CGImageRef from ALAsset instance and check width/height ratio.
ALAsset *alasset = ...
CFImageRef = alasset.thumbnail; // return square thumbnail and not suitable for me
CFImageRef = alasset.aspectRationThumbnail; //return aspect ration thumbnail, but very slowly
It isn't suitable for us, because it works slowly for many files.
Also we have tried to get metadata from defaultRepresentation and check image EXIF, but it works slowly to.
NSDictionary *dictionary = [alasset defaultRepresentation] metadata]; //very slowly to
Is there any way to make it better?
Thanks
Finaly, I've found this solution for ALAsset:
ALAssetsLibrary *assetsLibrary = ...;
NSOperation *queue = [NSoperationQueue alloc] init];
static NSString * const kAssetQueueName = ...;
static NSUInteger const kAssetConcurrentOperationCount = ...; //I use 5
queue.maxConcurrentOperationCount = kAssetConcurrentOperationCount;
queue.name = kAssetQueueName;
dispatch_async(dispatch_get_main_queue(), ^{
[assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
/*You must check the group is not nil */
if (!group)
return;
/*Then you need to select group where you will search panoramas: for iPhone-Simulator it's #"Saved Photos" and "Camera Roll" for iPhone. It's actuality only for iOS 7 or early. */
static NSString * const kAssetGroupName = ...;
if ([[group valueForProperty:ALAssetsGroupPropertyName] kAssetGroupName]) {
[group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop) {
if (!asset)
return;
[queue addOperationWithBlock:^{
//I use #autoreleasepool for instant memory release, after I find panoramas asset url
#autoreleasepool {
ALAssetRepresentation *defaultRepresentation = asset.defaultRepresentation;
if ([defaultRepresentation.UTI isEqualToString:#"public.jpeg"]) {
NSDictionary *metadata = defaultRepresentation.metadata;
if (!metadata)
return;
if (metadata[#"PixelWidth"] && metadata[#"PixelHeight"]) {
NSInteger pixelWidth = [metadata[#"PixelWidth"] integerValue];
NSInteger pixelHeight = [metadata[#"PixelHeight"] integerValue];
static NSUInteger const kSidesRelationshipConstant = ...; //I use 2
static NSUInteger const kMinimalPanoramaHeight = ...; //I use 600
if (pixelHeight >= kMinimalPanoramaHeight && pixelWidth/pixelHeight >= kSidesRelationshipConstant) {/*So, that is panorama.*/}
}
}];
}];
}
} failureBlock:^(NSError *error) {
//Some failing action, you know.
}];
};
That is. So, I think that is not the best solution. However, for today I have not found any better.
I am using ALAssetLibrary to access camera roll. But it is getting all images, like what's App images, Facebook Image etc.
My code like this:
[_library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if (group) {
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
[group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop) {
if (asset) {
//Getting photos here
}];
} } failureBlock:^(NSError *error) {
NSLog(#"Failed.");
}];
Is there any way to get only Camera capture photos using ALAssetLibrary ?
The only difference between camera captured photos and WhatsApp images is the EXIF Data.
You can read it out with
ALAssetRepresentation *representation = [asset defaultRepresentation];
NSDictionary *meta = [representation metadata];
or in Swift:
var representation = asset.defaultRepresentation()
var meta = representation.metadata()
This returns the following:
{TIFF}: {
DateTime = "2014:04:01 20:33:59";
Make = Apple;
Model = "iPhone 5";
Orientation = 3;
ResolutionUnit = 2;
Software = "7.1";
XResolution = 72;
YResolution = 72;
}, PixelWidth: 3264]
So you can check if Make is Apple, for WhatsApp images it is empty:
if([metaData["{TIFF}"]["Make"] isEqualToString: #"Apple"])
or in Swift:
if metaData["{TIFF}"]!["Make"] == "Apple"
I'm trying to create a custom image gallery within my iOS app. I would like to enable the user to be able to save certain meta data with the image so that it can be pulled up in the app later with the attached information.
First, when the user takes a picture, the app saves the image into a custom album for the app:
UITextField *nameField = [alertView textFieldAtIndex:0];
NSMutableDictionary *metaData = [[NSMutableDictionary alloc] init];
[metaData setObject:currentEvent forKey:kMetaDataEventKey];
[metaData setObject:[AppDelegate getActivePerson].name forKey:kMetaDataPersonKey];
[metaData setObject:nameField.text forKey:kMetaDataNameKey];
NSLog(#"Saving image with metadata: %#", metaData);
NSMutableDictionary *realMetaData = [[NSMutableDictionary alloc] init];
[realMetaData setObject:metaData forKey:kCGImagePropertyTIFFDictionary];
[library saveImage:imageToSave toAlbum:albumName metadata:realMetaData withCompletionBlock:^(NSError *error) {
if ( error != nil )
{
NSLog(#"Error saving picture? %#", error);
}
[self.tableView reloadData];
}];
Upon saving I get the following log message:
Saving image with metadata: {
Event = t;
Person = "George James";
PictureName = tt;
}
Then when I attempt to retrieve the images later, I use this function
-(void) loadAssets
{
self.assets = [NSMutableArray arrayWithCapacity:album.numberOfAssets];
[album enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if ( result != nil )
{
NSDictionary *metaData = result.defaultRepresentation.metadata;
NSLog(#"Retrieved image metadata: %#", metaData);
}
else
{
[self.tableView reloadData];
}
}];
}
But the log indicates that it did not successfully save the meta data associated with the image:
Retrieved image metadata: {
ColorModel = RGB;
DPIHeight = 72;
DPIWidth = 72;
Depth = 8;
Orientation = 1;
PixelHeight = 720;
PixelWidth = 960;
"{Exif}" = {
ColorSpace = 1;
ComponentsConfiguration = (
1,
2,
3,
0
);
ExifVersion = (
2,
2,
1
);
FlashPixVersion = (
1,
0
);
PixelXDimension = 960;
PixelYDimension = 720;
SceneCaptureType = 0;
};
"{TIFF}" = {
Orientation = 1;
ResolutionUnit = 2;
XResolution = 72;
YResolution = 72;
"_YCbCrPositioning" = 1;
};
}
library is an ALAssetsLibrary instance, and the saveImage: toAlbum: method is from this blog post, only slightly modified so that I can save metadata as such:
-(void)saveImage:(UIImage *)image toAlbum:(NSString *)albumName metadata:(NSDictionary *)metadata withCompletionBlock:(SaveImageCompletion)completionBlock
{
//write the image data to the assets library (camera roll)
[self writeImageToSavedPhotosAlbum:image.CGImage
metadata:metadata
completionBlock:^(NSURL* assetURL, NSError* error) {
//error handling
if (error!=nil) {
completionBlock(error);
return;
}
//add the asset to the custom photo album
[self addAssetURL: assetURL
toAlbum:albumName
withCompletionBlock:completionBlock];
}
];
}
The image is coming from a UIImagePickerController that uses the camera. The picture is successfully being saved to the correct album, just missing the metadata.
Am I doing something wrong in the save/load process? Am I actually not allowed to save custom meta data to an image?
I did some testing, and from what I can tell, the short answer is 'you can't do that.' It looks like the metadata has to conform to specific EXIF Metadata keys. You could look up the available TIFF Metadata keys and see if there are any values you want to set/overwrite. You could try, for example, using kCGImagePropertyTIFFImageDescription to store your data.
NSMutableDictionary *tiffDictionary= [[NSMutableDictionary alloc] init];
NSMutableDictionary *myMetadata = [[NSMutableDictionary alloc] init];
[tiffDictionary setObject:#"My Metadata" forKey:(NSString*)kCGImagePropertyTIFFImageDescription];
[myMetadata setObject:tiffDictionary forKey:(NSString*)kCGImagePropertyTIFFDictionary];
... and save myMetadata with the image.
For other keys, see this:
http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Reference/CGImageProperties_Reference/Reference/reference.html
Otherwise, what I would do is create an NSDictionary that uses an image's unique identifier as a key, and store the metadata object as the value. Save/Load this NSDictionary whenever you save/load an image.
I would like to retrieve the storage information programmatically like capacity, available storage, total number of apps, videos, pics etc.. Thanks in advance..
Try these. Not guaranteed to work on non-jailbroken devices though.
- (NSNumber *) totalDiskSpace
{
NSDictionary *fattributes = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil];
return [fattributes objectForKey:NSFileSystemSize];
}
- (NSNumber *) freeDiskSpace
{
NSDictionary *fattributes = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil];
return [fattributes objectForKey:NSFileSystemFreeSize];
}
To count number of files in a directory (including it's sub directories, I've used this (which isn't the most efficient way):
-(NSString *)numberOfSongs
{
NSString *musicPath = #"/var/mobile/Media/iTunes_Control/Music/";
NSArray *dirs = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:musicPath error:nil];
NSArray *subs = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:musicPath error:nil];
int totalFiles;
int subT = [subs count];
int dirT = [dirs count];
totalFiles = subT - dirT;
return [NSString stringWithFormat:#"%i", totalFiles];
}
Looks like WrightsCS answered disk space question.
If you want number of images, check out ALAssetsLibrary of the AssetsLibrary.framework (you'll have to include this framework in your "Link Binary With Libraries" section of the Target settings) and then:
#import <AssetsLibrary/AssetsLibrary.h>
// get the image assets
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
NSAssert(library, #"Unable to open ALAssetsLibrary");
NSUInteger __block images = 0;
[library enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:^(ALAssetsGroup *group, BOOL *stop){
NSLog(#"%#", group);
images += group.numberOfAssets;
dispatch_async(dispatch_get_main_queue(), ^{
// update my UI with the number of images
});
}
failureBlock:^(NSError *err){
NSLog(#"err=%#", err);
}];
If you want to access the iTunes library on non-jailbroken devices, check out the iPod Library Access Programming Guide, which shows you how to use MPMediaQuery (remember to include the MediaPlayer.framework in your project), and then:
#import <MediaPlayer/MediaPlayer.h>
MPMediaQuery *everything = [[MPMediaQuery alloc] init];
NSAssert(everything, #"Unable to open MPMediaQuery");
iTunesMediaCount = [[everything items] count];
I don't know if there's a published API for getting the number of apps. There are solutions for jailbroken devices, but I don't know about the rest of us.
You didn't ask about this, but if you want available RAM (not flash storage, but memory available for apps), you can get it via:
#import <mach/mach.h>
#import <mach/mach_host.h>
- (void)determineMemoryUsage
{
mach_port_t host_port;
mach_msg_type_number_t host_size;
vm_size_t pagesize;
host_port = mach_host_self();
host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
host_page_size(host_port, &pagesize);
vm_statistics_data_t vm_stat;
if (host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size) != KERN_SUCCESS)
NSLog(#"Failed to fetch vm statistics");
/* Stats in bytes */
natural_t mem_used = (vm_stat.active_count +
vm_stat.inactive_count +
vm_stat.wire_count) * pagesize;
natural_t mem_free = vm_stat.free_count * pagesize;
natural_t mem_total = mem_used + mem_free;
// do whatever you want with mem_used, mem_free, and mem_total
}
I saw several people on SO have been using this code successfully. But I got the incompatible block pointer error:
Incompatible block pointer types initializing
void(^)(struct ALAssetsGroup *, BOOL *)
with an expression of type
void(^)(ALAsset *, NSUInteger, BOOL *)
Any hints? (EDIT with complete code)
ALAssetsLibrary *library =[[ALAssetsLibrary alloc]init];
void (^assetEnumerator)(struct ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *result, NSUInteger index, BOOL *stop){
if(result != NULL) {
NSLog(#"See Asset: %#", result);
}
};
void (^assetGroupEnumerator)(struct ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) {
if(group != nil) {NSLog(#"dont See Asset: ");
[group enumerateAssetsUsingBlock:assetEnumerator];
}
};
[library enumerateGroupsWithTypes:ALAssetsGroupAlbum
usingBlock:assetGroupEnumerator
failureBlock: ^(NSError *error) {
NSLog(#"Failure");
}];
OK, newbie at blocks... but I found another example of an asset group enumerator block on here, and it didn't have struct in the declaration. I tried removing it from the code above, and it still works fine and doesn't have the error message. Hopefully someone who understands struct better can explain?
try changing this line:
void (^assetGroupEnumerator)(struct ALAssetsGroup *, BOOL *)
= ^(ALAssetsGroup *group, BOOL *stop)
to this:
void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *)
= ^(ALAssetsGroup *group, BOOL *stop)
I think the bottom line is that the ALAssetsLibrary enumerateGroupsWithTypes: usingBlock: expects a block looking like (ALAssetsGroup *, BOOL *) not (struct ALAssetsGroup *, BOOL *).
The difference between the expected and the actual type is just the work struct, i.e. struct ALAsset* vs. ALAsset*. (In your textual description it looks like a mismatch between ALAsset and ALAssetGroups, but I think you made a mistake in copying the error message.)
I don't quite understand where these differences come from (possibly due to the use of C++ somewhere?).
Anyway, the best solution is to use the type definition ALAssetsGroupEnumerationResultsBlock or ALAssetsLibraryGroupsEnumerationResultsBlock respectively, e.g.:
ALAssetsGroupEnumerationResultsBlock assetEnumerator = ^(ALAsset *result, NSUInteger index, BOOL *stop){
if (result != NULL) {
NSLog(#"See Asset: %#", result);
}
};