I'm trying to get some data about a video i chose in the UIImagePicker.
So when it gets into the UIImagePicker delegate method (below) i understand i need to use the UIImagePickerControllerReferenceURL key from the info dictionary.
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSURL *url = [info objectForKey:UIImagePickerControllerReferenceURL];
NSLog(#"%#",url);
[library addAssetURL:[info objectForKey:UIImagePickerControllerReferenceURL] toAlbum:#"Compedia" withCompletionBlock:^(NSError *error) {
if (error!=nil) {
NSLog(#"Big error: %#", [error description]);
}
}];
}
The problem is that the url is resulting with nil.
I printed the description of info and it looks like this:
Printing description of info:
{
UIImagePickerControllerMediaType = "public.movie";
UIImagePickerControllerMediaURL = "file://localhost/private/var/mobile/Applications/6630FBD3-1212-4ED0-BC3B-0C23AEEFB267/tmp/capture-T0x1f57e880.tmp.Ulfn5o/capturedvideo.MOV";
}
After i've done some research i found out that if i set the camera with kUTTypeMovie it should give me both media and reference url.
This is how i defined my camera:
cameraUI = [[UIImagePickerController alloc] init];
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
cameraUI.mediaTypes = [[NSArray alloc] initWithObjects:(NSString *)kUTTypeMovie, nil];
cameraUI.allowsEditing = YES;
cameraUI.delegate = delegate;
cameraUI.showsCameraControls = NO;
cameraUI.cameraOverlayView = [self getCustomToolBar];
Is there something i'm missing?
Thanks,
Nearly went insane on this one. Every where I looked everyone seemed to be convinced that the UIImagePickerControllerReferenceURL was always there.
Anyway the issue is that if your delegate is being called after you have taken a pic using the camera than the UIImagePickerControllerReferenceURL object will not be there as the image has not been saved to the camera roll yet so therefore there is no UIImagePickerControllerReferenceURL. It is only there when the delegate is called after selecting an image from the camera roll.
So my way around this is to use the ALAssetLibrary to save it to the camera roll then read back the URL.
Heres my solution:
First you want to detect your PickerController sourceType to see if it was the camera or photosLibrary/savedPhotosAlbum. If it is the camera then we use the Asset library's writeImageToSavedPhotosAlbum:orientation:completionBlock: to write the image to the camera roll and give us back the images URL in the completion block.
However if the its not the camera then that means is was either the photosLibrary or savedPhotosAlbum which in both cases [info objectForKey:UIImagePickerControllerReferenceURL] would return a valid url
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
//dismiss the imagepicker view
[self dismissViewControllerAnimated:YES completion:nil];
if( [picker sourceType] == UIImagePickerControllerSourceTypeCamera )
{
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
ALAssetsLibrary *library = [Utils defaultAssetsLibrary];
[library writeImageToSavedPhotosAlbum:image.CGImage orientation:(ALAssetOrientation)image.imageOrientation completionBlock:^(NSURL *assetURL, NSError *error )
{
//here is your URL : assetURL
}];
}
else
{
//else this is valid : [info objectForKey:UIImagePickerControllerReferenceURL]];
}
}
Hope that helps.
ALAssetsLibrary is deprecated... use PHFetchOptions..
Refer below answer:
https://stackoverflow.com/a/44328085/5315917
In my app, i have Used UiimagepickerController for taking Video.in bettween my programm received any web service Which belongs to my app,
i have to stop Video Capture and save video.
i have Used StopVideoCapture to do above thing ,but it doesn't call delegate - `
(void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
How to force call above delegate ??.or How to handle interruption Handling inUIImagePickerController`.. any idea?
The idea with delegate methods is not that you call those methods - "They call you".
So I would not consider calling the delegate method yourself a good practise. However, if you present the UIImagePickerViewController with a modal dialogue (which I guess is common for such a picker) then you can close it like this outside of your delegate method:
[[yourPicker parentViewController] dismissModalViewControllerAnimated:YES];
Source
Update: You can use the ALAssetsLibrary for accessing the stored data in your iPhone media library. I recently had to do a similar project where I had to list all images on the iPhone. The Github project ELCImagePickerController.git was very useful since it shows how the items in your library can be accessed. So you'll do something like this:
#import <AssetsLibrary/AssetsLibrary.h>
// ....
-(void)fetchPhotoAlbums{
if(!self.assetsGroups){
self.assetsGroups = [NSMutableDictionary dictionary];
}
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
NSMutableArray *returnArray = [[NSMutableArray alloc] init];
#autoreleasepool {
void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop){
if (group == nil){
// Completed
[self.delegate pictureService:self fetchedAlbums:returnArray];
return;
}
Album *currentAlbum = [self albumForAssetsGroup:group];
// Store the Group for later retrieving the pictures for the album
[self.assetsGroups setObject:group forKey:currentAlbum.identifier];
[returnArray addObject:currentAlbum];
[self.delegate pictureService:self fetchedAlbums:returnArray];
};
void (^assetGroupEnumberatorFailure)(NSError *) = ^(NSError *error) {
NSLog(#"A problem occured %#", [error description]);
};
[library enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:assetGroupEnumerator
failureBlock:assetGroupEnumberatorFailure];
}
}
-(void)fetchPhotosForAlbum:(Album *)album{
ALAssetsGroup *currentGroup = [self.assetsGroups objectForKey:album.identifier];
NSMutableArray *photos = [NSMutableArray array];
[currentGroup enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop){
if(asset == nil){
[self.delegate pictureService:self fetchedPictures:photos forAlbum:album];
return;
}
[photos addObject:[self pictureForAsset:asset]];
}];
}
Additionally I use two mapper methods to convert the AL-classes into my own model classes.
- (Album *)albumForAssetsGroup:(ALAssetsGroup *)assetsGroup{
Album *album = [[Album alloc] init];
album.title = [assetsGroup valueForProperty:ALAssetsGroupPropertyName];
album.identifier = [assetsGroup valueForProperty: ALAssetsGroupPropertyPersistentID];
album.assetsCount = assetsGroup.numberOfAssets;
album.thumbnail = [UIImage imageWithCGImage:assetsGroup.posterImage];
return album;
}
- (Picture *)pictureForAsset:(ALAsset *)asset{
Picture *picture = [[Picture alloc]init];
picture.identifier = [((NSArray *)[asset valueForProperty: ALAssetPropertyRepresentations]) objectAtIndex:0];
picture.thumbnail = [UIImage imageWithCGImage:asset.thumbnail];
return picture;
}
See the AssetsLibrary Documentation
I have an application where I recording a video. But when the recording is finished, I can't save the video immediately. I need to show an agreement first. So I try to save the URL i get from the image picker. And save the video to the library later.
This worked fine in iOS4, but not in iOS5.
I'm new to iOS and Objective-C so I probably made some totally wrong declaration of the property that is supposed to hold the URL.
This is some of the code:
.h
#import <UIKit/UIKit.h>
#import <AssetsLibrary/AssetsLibrary.h>
#interface Video_recViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIActionSheetDelegate> {
NSURL *tempMoviePath;
}
#property (nonatomic, retain) NSURL *tempMoviePath;
.m
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSURL *moviePath = [info objectForKey:UIImagePickerControllerMediaURL];
[self dismissModalViewControllerAnimated: YES];
NSLog(#"path from image picker: %#", moviePath);
tempMoviePath = moviePath;
NSLog(#"temp movie path: %#", tempMoviePath);
//
[self performSelector:#selector(showAgree) withObject:nil afterDelay:0.5];
}
- (void)userAgreed {
NSLog(#"user agreed");
//NSLog(#"temp movie path: %#", tempMoviePath);
[self saveMyVideo:tempMoviePath];
//[self performSelector:#selector(showSurvey) withObject:nil afterDelay:0.5];
}
- (void)saveMyVideo:(NSURL *)videoURL {
NSLog(#"saving movie at: %#", videoURL);
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:videoURL])
{
[library writeVideoAtPathToSavedPhotosAlbum:videoURL
completionBlock:^(NSURL *assetURL, NSError *error){}
];
}
[library release];
}
Output from log when didFinishPickingMediaWithInfo is:
temp movie path: file://localhost/private/var/mobile/Applications/8CFD1CB7-70A0-465C-B730-817ACE5A4F78/tmp/capture-T0x119660.tmp.hNFzkY/capturedvideo.MOV
Output from the log when doing "saveMyVideo". The URL has suddenly turn into this!! :
saving movie at: (
"0.31269",
"0.32899",
"0.63999",
"0.33001",
"0.3",
"0.6",
"0.15",
"0.05999"
)
(Answered by the OP in a question edit. See Question with no answers, but issue solved in the comments (or extended in chat) )
The OP wrote:
The wrong code was:
tempMoviePath = moviePath;
Because I'm setting a declared property I must use the set & get methods. It should be:
[self setTempMoviePath:moviePath];
Apparently iOS 4 wasn't so hard on this, but iOS5 can't handle it. But, anyway, it was wrong writing like that. I admit my mistake. :)
I used the following code to record video.
UIImagePickerController *m_objpicker;=[[UIImagePickerController alloc] init];
m_objpicker.sourceType = UIImagePickerControllerSourceTypeCamera;
m_objpicker.mediaTypes = [NSArray arrayWithObject:(NSString *)kUTTypeMovie];
// hide the camera controls
//picker.showsCameraControls=NO;
m_objpicker.delegate = self;
//picker.allowsImageEditing = NO;
m_objpicker.allowsEditing=NO;
// and put our overlay view in
//picker.cameraOverlayView=m_objOverlayView;
[self presentModalViewController:m_objpicker animated:YES];
When we finish recording
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
NSURL *m_objMediaURL=[info objectForKey:UIImagePickerControllerMediaURL];
[m_objpicker dismissModalViewControllerAnimated:YES];
}
My doubt is, how to save the captured video to a location we specify. Also how to use
UISaveVideoAtPathToSavedPhotosAlbum .
What all things i need to change in my code so that i can save video to a specified location
Thanks,
If you would like to save to the Camera Roll photos/videos album on the phone:
Definition:
void UISaveVideoAtPathToSavedPhotosAlbum (
NSString *videoPath,
id completionTarget,
SEL completionSelector,
void *contextInfo
);
Where and how to execute it:
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
.... code here .....
NSString* m_objMediaURL= [info objectForKey:UIImagePickerControllerMediaURL];
//remember to test that the video is compatible for saving to the photos album
UISaveVideoAtPathToSavedPhotosAlbum(m_objMediaURL, self, #selector(video:didFinishSavingWithError:contextInfo:), nil);
.... code here .....
}
There is no control beyond saving this to the Camera Roll re: location unless you want to save to the application bundle which I do not recommend.
You can save video to a location that you specify, please check following link how to capture video in iphone
To the Save the Video in Photo Album:
-(void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSString *tempFilePath = [[info objectForKey:UIImagePickerControllerMediaURL] path];
if (_newMedia){
if ( UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(tempFilePath))
{
// Copy it to the camera roll.
UISaveVideoAtPathToSavedPhotosAlbum(tempFilePath, self, #selector(video:didFinishSavingWithError:contextInfo:), (__bridge void *)(tempFilePath));
}
}
-(void) video: (NSString *) videoPath
didFinishSavingWithError: (NSError *) error
contextInfo: (void *) contextInfo {
NSLog(#"Finished saving video with error: %#", error);
}
It's well known that UIImagePickerController doesn't return the metadata of the photo after selection. However, a couple of apps in the app store (Mobile Fotos, PixelPipe) seem to be able to read the original files and the EXIF data stored within them, enabling the app to extract the geodata from the selected photo.
They seem to do this by reading the original file from the /private/var/mobile/Media/DCIM/100APPLE/ folder and running it through an EXIF library.
However, I can't work out a way of matching a photo returned from the UIImagePickerController to a file on disk. I've explored file sizes, but the original file is a JPEG, whilst the returned image is a raw UIImage, making it impossible to know the file size of the image that was selected.
I'm considering making a table of hashes and matching against the first x pixels of each image. This seems a bit over the top though, and probably quite slow.
Any suggestions?
Have you took a look at this exif iPhone library?
http://code.google.com/p/iphone-exif/
Gonna try it on my side. I'd like to get the GPS (geotags) coordinates from the picture that has been taken with the UIImagePickerController :/
After a deeper look, this library seems to take NSData info as an input and the UIImagePickerController returns a UIImage after taking a snapshot. In theory, if we use the selected from the UIkit category for UIImage
NSData * UIImageJPEGRepresentation (
UIImage *image,
CGFloat compressionQuality
);
Then we can convert the UIImage into a NSData instance and then use it with the iPhone exif library.
UPDATE:
I gave a test to the library mentioned above and it seems to work. However because of my limited knwoledge about the EXIF format and the lack of high level API in the library, I don't manage to get the values for the EXIF tags.
Here's my code in case any of you can go further :
#import "EXFJpeg.h"
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo {
NSLog(#"image picked %# with info %#", image, editingInfo);
NSData* jpegData = UIImageJPEGRepresentation (image,0.5);
EXFJpeg* jpegScanner = [[EXFJpeg alloc] init];
[jpegScanner scanImageData: jpegData];
EXFMetaData* exifData = jpegScanner.exifMetaData;
EXFJFIF* jfif = jpegScanner.jfif;
EXFTag* tagDefinition = [exifData tagDefinition: [NSNumber numberWithInt:EXIF_DateTime]];
//EXFTag* latitudeDef = [exifData tagDefinition: [NSNumber numberWithInt:EXIF_GPSLatitude]];
//EXFTag* longitudeDef = [exifData tagDefinition: [NSNumber numberWithInt:EXIF_GPSLongitude]];
id latitudeValue = [exifData tagValue:[NSNumber numberWithInt:EXIF_GPSLatitude]];
id longitudeValue = [exifData tagValue:[NSNumber numberWithInt:EXIF_GPSLongitude]];
id datetime = [exifData tagValue:[NSNumber numberWithInt:EXIF_DateTime]];
id t = [exifData tagValue:[NSNumber numberWithInt:EXIF_Model]];
....
....
The retrieving of tags definition is OK, but all tag values returns nil :(
In case you want to give a try to the library, you need to define a global variable to get it running (as explained in the doc but hum.. :/)
BOOL gLogging = FALSE;
UPDATE 2
Answer here : iPhone - access location information from a photo
A UIImage does not encapsulate the meta information, so we're stuck : for sure, no EXIF info will be given through this interface.
FINAL UPDATE
Ok I managed to get it working, at least to geotag properly pictures returned by the picker.
Before triggering the UIImagePickerController, it's up to you to use the CLLocationManager to retrieve the current CLocation
Once you have it, you can use this method that uses exif-iPhone library to geotag the UIImage from the CLLocation :
-(NSData*) geotagImage:(UIImage*)image withLocation:(CLLocation*)imageLocation {
NSData* jpegData = UIImageJPEGRepresentation(image, 0.8);
EXFJpeg* jpegScanner = [[EXFJpeg alloc] init];
[jpegScanner scanImageData: jpegData];
EXFMetaData* exifMetaData = jpegScanner.exifMetaData;
// end of helper methods
// adding GPS data to the Exif object
NSMutableArray* locArray = [self createLocArray:imageLocation.coordinate.latitude];
EXFGPSLoc* gpsLoc = [[EXFGPSLoc alloc] init];
[self populateGPS: gpsLoc :locArray];
[exifMetaData addTagValue:gpsLoc forKey:[NSNumber numberWithInt:EXIF_GPSLatitude] ];
[gpsLoc release];
[locArray release];
locArray = [self createLocArray:imageLocation.coordinate.longitude];
gpsLoc = [[EXFGPSLoc alloc] init];
[self populateGPS: gpsLoc :locArray];
[exifMetaData addTagValue:gpsLoc forKey:[NSNumber numberWithInt:EXIF_GPSLongitude] ];
[gpsLoc release];
[locArray release];
NSString* ref;
if (imageLocation.coordinate.latitude <0.0)
ref = #"S";
else
ref =#"N";
[exifMetaData addTagValue: ref forKey:[NSNumber numberWithInt:EXIF_GPSLatitudeRef] ];
if (imageLocation.coordinate.longitude <0.0)
ref = #"W";
else
ref =#"E";
[exifMetaData addTagValue: ref forKey:[NSNumber numberWithInt:EXIF_GPSLongitudeRef] ];
NSMutableData* taggedJpegData = [[NSMutableData alloc] init];
[jpegScanner populateImageData:taggedJpegData];
[jpegScanner release];
return [taggedJpegData autorelease];
}
// Helper methods for location conversion
-(NSMutableArray*) createLocArray:(double) val{
val = fabs(val);
NSMutableArray* array = [[NSMutableArray alloc] init];
double deg = (int)val;
[array addObject:[NSNumber numberWithDouble:deg]];
val = val - deg;
val = val*60;
double minutes = (int) val;
[array addObject:[NSNumber numberWithDouble:minutes]];
val = val - minutes;
val = val*60;
double seconds = val;
[array addObject:[NSNumber numberWithDouble:seconds]];
return array;
}
-(void) populateGPS:(EXFGPSLoc* ) gpsLoc :(NSArray*) locArray{
long numDenumArray[2];
long* arrPtr = numDenumArray;
[EXFUtils convertRationalToFraction:&arrPtr :[locArray objectAtIndex:0]];
EXFraction* fract = [[EXFraction alloc] initWith:numDenumArray[0]:numDenumArray[1]];
gpsLoc.degrees = fract;
[fract release];
[EXFUtils convertRationalToFraction:&arrPtr :[locArray objectAtIndex:1]];
fract = [[EXFraction alloc] initWith:numDenumArray[0] :numDenumArray[1]];
gpsLoc.minutes = fract;
[fract release];
[EXFUtils convertRationalToFraction:&arrPtr :[locArray objectAtIndex:2]];
fract = [[EXFraction alloc] initWith:numDenumArray[0] :numDenumArray[1]];
gpsLoc.seconds = fract;
[fract release];
}
This works with iOS5 (beta 4) and the camera roll (you need type defs for the blocks in the .h):
-(void) imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:(NSString*)kUTTypeImage]) {
NSURL *url = [info objectForKey:UIImagePickerControllerReferenceURL];
if (url) {
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset) {
CLLocation *location = [myasset valueForProperty:ALAssetPropertyLocation];
// location contains lat/long, timestamp, etc
// extracting the image is more tricky and 5.x beta ALAssetRepresentation has bugs!
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror) {
NSLog(#"cant get image - %#", [myerror localizedDescription]);
};
ALAssetsLibrary *assetsLib = [[ALAssetsLibrary alloc] init];
[assetsLib assetForURL:url resultBlock:resultblock failureBlock:failureblock];
}
}
There is a way in iOS 8
Without using any 3rd party EXIF library.
#import <Photos/Photos.h>
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSURL *url = [info objectForKey:UIImagePickerControllerReferenceURL];
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithALAssetURLs:#[url] options:nil];
PHAsset *asset = fetchResult.firstObject;
//All you need is
//asset.location.coordinate.latitude
//asset.location.coordinate.longitude
//Other useful properties of PHAsset
//asset.favorite
//asset.modificationDate
//asset.creationDate
}
Apple has added an Image I/O Framework in iOS4 which can be used to read EXIF data from pictures. I don't know if the UIImagePickerController returns a picture with the EXIF data embedded though.
Edit: In iOS4 you can fetch the EXIF data by grabbing the value of the UIImagePickerControllerMediaMetadata key in the info dictionary which is passed to the UIImagePickerControllerDelegate delegate.
I had a similar question where I wanted just the date a picture was taken and none of the above appear to solve my problem in a simple way (e.g. no external libraries), so here is all of the data I could find which you can extract from an image after selecting it with the picker:
// Inside whatever implements UIImagePickerControllerDelegate
#import AssetsLibrary;
// ... your other code here ...
#implementation MYImagePickerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *mediaType = info[UIImagePickerControllerMediaType];
UIImage *originalImage = info[UIImagePickerControllerOriginalImage];
UIImage *editedImage = info[UIImagePickerControllerEditedImage];
NSValue *cropRect = info[UIImagePickerControllerCropRect];
NSURL *mediaUrl = info[UIImagePickerControllerMediaURL];
NSURL *referenceUrl = info[UIImagePickerControllerReferenceURL];
NSDictionary *mediaMetadata = info[UIImagePickerControllerMediaMetadata];
NSLog(#"mediaType=%#", mediaType);
NSLog(#"originalImage=%#", originalImage);
NSLog(#"editedImage=%#", editedImage);
NSLog(#"cropRect=%#", cropRect);
NSLog(#"mediaUrl=%#", mediaUrl);
NSLog(#"referenceUrl=%#", referenceUrl);
NSLog(#"mediaMetadata=%#", mediaMetadata);
if (!referenceUrl) {
NSLog(#"Media did not have reference URL.");
} else {
ALAssetsLibrary *assetsLib = [[ALAssetsLibrary alloc] init];
[assetsLib assetForURL:referenceUrl
resultBlock:^(ALAsset *asset) {
NSString *type =
[asset valueForProperty:ALAssetPropertyType];
CLLocation *location =
[asset valueForProperty:ALAssetPropertyLocation];
NSNumber *duration =
[asset valueForProperty:ALAssetPropertyDuration];
NSNumber *orientation =
[asset valueForProperty:ALAssetPropertyOrientation];
NSDate *date =
[asset valueForProperty:ALAssetPropertyDate];
NSArray *representations =
[asset valueForProperty:ALAssetPropertyRepresentations];
NSDictionary *urls =
[asset valueForProperty:ALAssetPropertyURLs];
NSURL *assetUrl =
[asset valueForProperty:ALAssetPropertyAssetURL];
NSLog(#"type=%#", type);
NSLog(#"location=%#", location);
NSLog(#"duration=%#", duration);
NSLog(#"assetUrl=%#", assetUrl);
NSLog(#"orientation=%#", orientation);
NSLog(#"date=%#", date);
NSLog(#"representations=%#", representations);
NSLog(#"urls=%#", urls);
}
failureBlock:^(NSError *error) {
NSLog(#"Failed to get asset: %#", error);
}];
}
[picker dismissViewControllerAnimated:YES
completion:nil];
}
#end
So when you select an image, you get output that looks like this (including date!):
mediaType=public.image
originalImage=<UIImage: 0x7fb38e00e870> size {1280, 850} orientation 0 scale 1.000000
editedImage=<UIImage: 0x7fb38e09e1e0> size {640, 424} orientation 0 scale 1.000000
cropRect=NSRect: {{0, 0}, {1280, 848}}
mediaUrl=(null)
referenceUrl=assets-library://asset/asset.JPG?id=AC072879-DA36-4A56-8A04-4D467C878877&ext=JPG
mediaMetadata=(null)
type=ALAssetTypePhoto
location=(null)
duration=ALErrorInvalidProperty
assetUrl=assets-library://asset/asset.JPG?id=AC072879-DA36-4A56-8A04-4D467C878877&ext=JPG
orientation=0
date=2014-07-14 04:28:18 +0000
representations=(
"public.jpeg"
)
urls={
"public.jpeg" = "assets-library://asset/asset.JPG?id=AC072879-DA36-4A56-8A04-4D467C878877&ext=JPG";
}
Anyway, hopefully that saves someone else some time.
I spend a while working on this as well for an application I was contracted to build. Basically as the API currently stands it is not possible. The basic problem is the UIImage class STRIPS all EXIF data except for the orientation out. Also the function to save to the camera roll strips this data out. So basically the only way to grab and maintain any extra EXIF data is to save it in a private "camera roll" in your application. I have filed this bug with apple as well and emphasized the need to the app reviewer reps we've been in contact with. Hopefully someday they'll add it in.. Otherwise it makes having GEO tagging completely useless as it only works in the "stock" camera application.
NOTE Some applications on the app store hack around this. By, what I have found, directly accessing the camera roll and SAVING photos straight to it to save GEO data. However this only works with the camera roll/saved photos and NOT the rest of the photo library. The photos "synced" to your phone from your computer have all EXIF data except for orientation stripped.
I still can't understand why those applications were approved (heck they even DELETE from the camera roll) and our application which does none of that is still being held back.
For iOS 8 and later you can use Photos Framework.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let url = info[UIImagePickerControllerReferenceURL] as? URL
if url != nil {
let fetchResult = PHAsset.fetchAssets(withALAssetURLs: [url!], options: nil)
let asset = fetchResult.firstObject
print(asset?.location?.coordinate.latitude)
print(asset?.creationDate)
}
}
This is something that the public API does not provide, but could be useful to many people. Your primary recourse is to file a bug with Apple that describes what you need (and it can be helpful to explain why you need it as well). Hopefully your request could make it into a future release.
After filing a bug, you could also use one of the Developer Technical Support (DTS) incidents that came with your iPhone Developer Program membership. If there is a public way to do this, an Apple engineer will know. Otherwise, it may at least help get your plight a bit more attention within the mothership. Best of luck!
Use the UIImagePickerControllerMediaURL dictionary key to get the file URL to the original file. Despite what the documentation says, you can get the file URL for photos and not only movies.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
// Try to get the original file.
NSURL *originalFile = [info objectForKey:UIImagePickerControllerMediaURL];
if (originalFile) {
NSData *fileData = [NSData dataWithContentsOfURL:originalFile];
}
}
You might be able to hash the image data returned by the UIImagePickerController and each of the images in the directory and compare them.
Just a thought, but have you tried TTPhotoViewController in the Three20 project on GitHub?
That provides an image picker that can read from multiple sources. You may be able to use it as an alternative to UIImagePickerController, or the source might give you a clue how to work out how to get the info you need.
Is there a specific reason you want to extract the location data from the image? An alternative could be to get the location separately using the CoreLocation framework. If it's only the geodata you need, this might save you some headaches.
it seems that photo attained by UIImagePickerControllerMediaURL don't have exif tags at all
In order to get this metadata you'll have to use the lower level framework AVFoundation.
Take a look at Apple's Squarecam example (http://developer.apple.com/library/ios/#samplecode/SquareCam/Introduction/Intro.html)
Find the method below and add the line, I've added to the code. The metadata dictionary returned also contains a diagnostics NSDictionary object.
- (BOOL)writeCGImageToCameraRoll:(CGImageRef)cgImage withMetadata:(NSDictionary *)metadata
{
NSDictionary *Exif = [metadata objectForKey:#"Exif"]; // Add this line
}
I'm using this for camera roll images
-(CLLocation*)locationFromAsset:(ALAsset*)asset
{
if (!asset)
return nil;
NSDictionary* pickedImageMetadata = [[asset defaultRepresentation] metadata];
NSDictionary* gpsInfo = [pickedImageMetadata objectForKey:(__bridge NSString *)kCGImagePropertyGPSDictionary];
if (gpsInfo){
NSNumber* nLat = [gpsInfo objectForKey:(__bridge NSString *)kCGImagePropertyGPSLatitude];
NSNumber* nLng = [gpsInfo objectForKey:(__bridge NSString *)kCGImagePropertyGPSLongitude];
if (nLat && nLng)
return [[CLLocation alloc]initWithLatitude:[nLat doubleValue] longitude:[nLng doubleValue]];
}
return nil;
}
-(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
//UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
NSURL *assetURL = [info objectForKey:UIImagePickerControllerReferenceURL];
// create the asset library in the init method of your custom object or view controller
//self.library = [[ALAssetsLibrary alloc] init];
//
[self.library assetForURL:assetURL resultBlock:^(ALAsset *asset) {
// try to retrieve gps metadata coordinates
CLLocation* myLocation = [self locationFromAsset:asset];
// Do your stuff....
} failureBlock:^(NSError *error) {
NSLog(#"Failed to get asset from library");
}];
}
It works obviously if the image contains gps meta informations
Hope it helps
This is in Swift 3 if you still want support for iOS 8:
import AssetsLibrary
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if picker.sourceType == UIImagePickerControllerSourceType.photoLibrary,
let url = info[UIImagePickerControllerReferenceURL] as? URL {
let assetLibrary = ALAssetsLibrary()
assetLibrary.asset(for: url, resultBlock: { (asset) in
if let asset = asset {
let assetRep: ALAssetRepresentation = asset.defaultRepresentation()
let metaData: NSDictionary = assetRep.metadata() as NSDictionary
print(metaData)
}
}, failureBlock: { (error) in
print(error!)
})
}
}
For iOS 10 - Swift 3
The picker's callback has an info dict where there is a key with metadata: UIImagePickerControllerMediaMetadata
The naughty way to do this is to traverse the UIImagePickerViewController's views and pick out the selected image in the delegate callback.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
id thumbnailView = [[[[[[[[[[picker.view subviews]
objectAtIndex:0] subviews]
objectAtIndex:0] subviews]
objectAtIndex:0] subviews]
objectAtIndex:0] subviews]
objectAtIndex:0];
NSString *fullSizePath = [[[thumbnailView selectedPhoto] fileGroup] pathForFullSizeImage];
NSString *thumbnailPath = [[[thumbnailView selectedPhoto] fileGroup] pathForThumbnailFile];
NSLog(#"%# and %#", fullSizePath, thumbnailPath);
}
That will give you the path to the full size image, which you can then open with an EXIF library of your choice.
But, this calls a Private API and these method names will be detected by Apple if you submit this app. So don't do this, OK?