7
votes

I have an app that uses a UIImagePickerController to retrieve pictures both from camera and from the photos library. In the image picker delegate I only want to save the NSURL (UIImagePickerControllerReferenceURL) of the picked image to save memory. When the user needs to see the image later on, I load it with PHCachingImageManager directly from the photos library.

Now - this whole thing works great with pictures the user chooses from the library, but not with pictures directly taken by camera (since there is no URL). I am currently trying to save the picture with PHAsset, but I have no idea how to get the NSURL of the save picture.

This is what I've been up to:

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject])
{
    picker.dismissViewControllerAnimated(true, completion: nil)

    let pickedImage = info[UIImagePickerControllerOriginalImage] as! UIImage

    if picker.sourceType == .Camera
    {
        // When taking a picture with the camera, store it in the user roll
        PHPhotoLibrary.sharedPhotoLibrary().performChanges(
            { () -> Void in

                // save the image
                PHAssetCreationRequest.creationRequestForAssetFromImage(pickedImage)

                // TODO how to get the asset url

            }, completionHandler:
            { (finished, error) -> Void in
                if (finished)
                {

                }
            }
        )

    }
    else
    {
        let pickedImageUrl: NSURL? = info[UIImagePickerControllerReferenceURL] as? NSURL

        currentImageUrl = pickedImageUrl

        currentImage = pickedImage

        toggleImageInfoView(true)
        toggleMapState(true)
    }


}

Any ideas how to get the url of the saved picture?

Best, Georg

3
Georg, did you find a solution? I'm facing the same problem here and don't find any solution...gersonmdesouza

3 Answers

4
votes

UPDATE: Seems like I found an answer to this Problem.

Step 1: I save the image to the camera

UIImageWriteToSavedPhotosAlbum(image.image, self, #selector(cameraImageSavedAsynchronously), nil)

this is done asynchronously, so make sure to set a selector when operation has finished.

Step 2: When operation has completed, I do the following:

func fetchLastImage(completion: (localIdentifier: String?) -> Void)
{
    let fetchOptions = PHFetchOptions()
    fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
    fetchOptions.fetchLimit = 1

    let fetchResult = PHAsset.fetchAssetsWithMediaType(.Image, options: fetchOptions)
    if (fetchResult.firstObject != nil)
    {
        let lastImageAsset: PHAsset = fetchResult.firstObject as! PHAsset
        completion(localIdentifier: lastImageAsset.localIdentifier)
    }
    else
    {
        completion(localIdentifier: nil)
    }
}

I fetch the last image in camera roll with PHAsset and save the local identifier of the image. This is not an URL, but a unique identifier which does not change. This way, you can access the saved image perfectly.

Hope this helps others!

1
votes

I agree with you.

but, if the Image's Exif has the date of the earlier .

let fetchResult = PHAsset.fetchAssetsWithMediaType(.Image, options: fetchOptions)
fetchResult.firstObject

fetchResult.firstObject is not the one you just saved.

maybe you can modify fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false) to key: "modificationDate"

BTW, I found an other way:

 __block PHObjectPlaceholder *placeholderAsset = nil;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
    PHAssetChangeRequest *newAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:url];
    newAssetRequest.location = location;
    newAssetRequest.creationDate = [NSDate date];
    placeholderAsset = newAssetRequest.placeholderForCreatedAsset;
} completionHandler:^(BOOL success, NSError *error) {
    if(success){
        PHAsset *asset = [self getAssetFromlocalIdentifier:placeholderAsset.localIdentifier];
        completionBlock(asset, YES);
    } else {
        completionBlock(nil, NO);
    }
}];

can get the newly PHAsset.

0
votes

Ive updated the answer to include returning any asset type, as well as simpler/cleaner way of returning the asset.

Theres no need to a competition handler.

func fetchLastAsset() -> PHAsset? {
    let fetchOptions = PHFetchOptions()
    fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
    fetchOptions.fetchLimit = 1
    let fetchResult = PHAsset.fetchAssets(with: fetchOptions)
    return fetchResult.firstObject
}