I can load normal images: public.image types.
The Apple proRaw(adobe raw image type: DNG format) can be used in iPhone 12 series.
So, I captured with RAW image and I want to load the DNG file from app.
But I can't load the image using PHPicker.
Normally, the codes below.
PHPickerConfiguration *configuration = [[PHPickerConfiguration alloc] init];
configuration.filter = [PHPickerFilter anyFilterMatchingSubfilters:#[[PHPickerFilter imagesFilter], [PHPickerFilter livePhotosFilter]]];
PHPickerViewController *pickerController = [[PHPickerViewController alloc] initWithConfiguration:configuration];
pickerController.delegate = self;
[pickerController setModalPresentationStyle:UIModalPresentationCustom];
[pickerController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[viewController presentViewController:pickerController animated:YES completion:nil];
-(void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray<PHPickerResult *> *)results API_AVAILABLE(ios(14)) {
[picker dismissViewControllerAnimated:YES completion:nil];
PHPickerResult *result = [results firstObject];
if ([result.itemProvider canLoadObjectOfClass:[UIImage class]]) { // 1
[result.itemProvider loadObjectOfClass:[NSObject class] completionHandler:^(__kindof id<NSItemProviderReading> _Nullable object, NSError * _Nullable error) {
if ([object isKindOfClass:[UIImage class]]) {
UIImage *image = object;
...
}
}];
}
In comment 1 line, returned NO.
How to load raw image using PHPicker?
Using loadFileRepresentation to get the photo's data into a CGImage object worked for me. Something like:
result.itemProvider.loadFileRepresentation(forTypeIdentifier: "public.image") { url, _ in
guard let url = url,
let data = NSData(contentsOf: url),
let source = CGImageSourceCreateWithData(data, nil),
let cgImage = CGImageSourceCreateImageAtIndex(source, 0, nil) else {
// handle
}
let image = UIImage(cgImage)
...
}
or
[result.itemProvider loadFileRepresentationForTypeIdentifier:#"public.image" completionHandler:^(NSURL * _Nullable url, NSError * _Nullable error) {
if (url) {
NSData *data = [NSData dataWithContentsOfURL:url];
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source, 0, NULL);
UIImage *image = [UIImage imageWithCGImage:cgImage];
...
}
}];
You may need to get the correct orientation using CGImageSourceCopyPropertiesAtIndex to get the metadata dictionary, find the correct value with the kCGImagePropertyOrientation key, transform it from CGImagePropertyOrientation to UIImage.Orientation, and pass it to the UIImage initializer.
It's a bit more involved than just using loadObjectOfClass but it won't require photo access authorization.
You can use this code also. for opening new PHPicker.
For More Knowledge about PHPicker in WWDC21 PHPicker WWDC20 Video and PHPicker WWDC21 Video
WWDC PHPicker Notes PHPicker Notes
import Photos
import PhotosUI
// MARK: - PHPicker Configurations (PHPickerViewControllerDelegate)
extension ViewController: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: .none)
results.forEach { result in
result.itemProvider.loadObject(ofClass: UIImage.self) { reading, error in
guard let image = reading as? UIImage, error == nil else { return }
DispatchQueue.main.async {
self.profilePictureOutlet.image = image
// TODO: - Here you get UIImage
}
result.itemProvider.loadFileRepresentation(forTypeIdentifier: "public.image") { [weak self] url, _ in
// TODO: - Here You Get The URL
}
}
}
}
/// call this method for `PHPicker`
func openPHPicker() {
var phPickerConfig = PHPickerConfiguration(photoLibrary: .shared())
phPickerConfig.selectionLimit = 1
phPickerConfig.filter = PHPickerFilter.any(of: [.images, .livePhotos])
let phPickerVC = PHPickerViewController(configuration: phPickerConfig)
phPickerVC.delegate = self
present(phPickerVC, animated: true)
}
}
Related
I am tring to get PHAsset from UIImagePickerController and using following code:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// NSURL *imageURL = [info valueForKey:UIImagePickerControllerReferenceURL];
PHAsset *phAsset = [info valueForKey:UIImagePickerControllerPHAsset];
if (phAsset) {
[assetsArr addObject:phAsset];
[galleryArr addObject:[info valueForKey:UIImagePickerControllerOriginalImage]];
}
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self.galleryCollectionView reloadData];
[picker dismissViewControllerAnimated:YES completion:nil];
});
});
}
If source type is UIImagePickerControllerSourceTypePhotoLibrary then I am able to get PHAsset by above code. But if source type is UIImagePickerControllerSourceTypeCamera then PHAsset is nil.
How can i get PHAsset in that case?
If UIImagePickerControllerReferenceURL have some value then you can get asset using following code:
PHFetchResult<PHAsset *> *result = [PHAsset fetchAssetsWithALAssetURLs:[NSArray arrayWithObject:imageURL] options:nil];
It may have more than one asset select according to your requirement.
if you get the image from taking photo, you can get the origin image by Info Dictionary
then save the image at album
and get the PHAsset object.
the code was shown as below:
/// Save image to album.
static func saveImageToAlbum(image: UIImage, completion: ( (Bool, PHAsset?) -> Void )? ) {
let status = PHPhotoLibrary.authorizationStatus()
if status == .denied || status == .restricted {
completion?(false, nil)
return
}
var placeholderAsset: PHObjectPlaceholder? = nil
PHPhotoLibrary.shared().performChanges({
let newAssetRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
placeholderAsset = newAssetRequest.placeholderForCreatedAsset
}) { (suc, error) in
DispatchQueue.main.async {
if suc {
let asset = self.getAsset(from: placeholderAsset?.localIdentifier)
completion?(suc, asset)
} else {
completion?(false, nil)
}
}
}
}
private static func getAsset(from localIdentifier: String?) -> PHAsset? {
guard let id = localIdentifier else {
return nil
}
let result = PHAsset.fetchAssets(withLocalIdentifiers: [id], options: nil)
if result.count > 0{
return result[0]
}
return nil
}
I am trying (AND FAILING) to get the user to upload an image and then pass the image name and actual image to another controller. This worked fine on a simulator and an actual device before ios11. But now, it just works on the simulator and crashes every time on the actual device. I am using TestFlight to test this so I am unable to see the errors on the device. But I saw this and was able to create my method which looks like this:
#objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage
if #available(iOS 9.0, *) {
let url = info[UIImagePickerControllerReferenceURL] as! URL
//let assets = PHAsset.fetchAssets(withALAssetURLs: [url], options: nil)
//imageName = PHAssetResource.assetResources(for: assets.firstObject!).first!.originalFilename
let result = PHAsset.fetchAssets(withALAssetURLs: [url], options: nil)
imageName = PHAssetResource.assetResources(for: result.firstObject!).first!.originalFilename
// let asset = result.firstObject
// if(asset == nil){
// print("asset is NIL")
// }else {
// print("asset is not NIL")
// }
// print(asset?.value(forKey: "filename"))
// iconImageName = asset?.value(forKey: "filename") as! String
print("FILENAME START")
print(iconImageName)
print("FILENAME END")
} else {
// Fallback on earlier versions
}
self.dismiss(animated: true, completion: { () -> Void in
})
}
the commented out code are other ways I tried to get the file name. Why does this work on a simulator but not on a real device? I have looked online but this seems like the right way except it is not.
PS: Long story but my device does not work when connected to my Mac, which is why I am using TestFlight.
You can get fileName from UIImagePicker easily by this way:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let imageURL = info[UIImagePickerControllerReferenceURL] as? URL {
let result = PHAsset.fetchAssets(withALAssetURLs: [imageURL], options: nil)
let asset = result.firstObject
print(asset?.value(forKey: "filename"))
}
dismiss(animated: true, completion: nil)
}
For further information, you can follow this answer: https://stackoverflow.com/a/40628457/5167909
I believe you can get the image path(and from there the filename easily) from the info dictionary:
info[UIImagePickerControllerImageURL]
Try this:
PHAsset *asset = [self assetFromDictionary:info];
if (asset) {
NSData *fileData = nil;
NSString *fileName = [self filenameForAsset:asset];
}
- (PHAsset *)assetFromDictionary:(NSDictionary *)info {
PHAsset *asset = nil;
if (#available(iOS 11.0, *)) {
NSURL *assetURL = info[UIImagePickerControllerImageURL];
if (assetURL) {
asset = [info valueForKey:UIImagePickerControllerPHAsset];
}
} else {
NSURL *assetURL = info[UIImagePickerControllerReferenceURL];
if (assetURL) {
asset = [[PHAsset fetchAssetsWithALAssetURLs:#[assetURL] options:nil] lastObject];
}
}
return asset;
}
- (NSString *)filenameForAsset:(PHAsset *)asset {
NSString *filename = #"";
if (#available(iOS 9.0, *)) {
filename = [[PHAssetResource assetResourcesForAsset:asset] firstObject].originalFilename;
} else {
filename = [asset valueForKey:#"filename"];
}
return filename;
}
How can I get the name of video file selected from Camera roll or any other album in UIImagePickerController's delegate method ?
I'm able to get the name of image but if using same in video it's returning nil.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
if (![mediaType isEqualToString:(NSString *)kUTTypeMovie])
return;
mediaURl = [info objectForKey:UIImagePickerControllerMediaURL];
//NSLog(#"mediaURL %#",mediaURl);
moviePath = mediaURl.absoluteString;
// NSLog(#"moviePath %#",moviePath);
tempFilePath = [[info objectForKey:UIImagePickerControllerMediaURL] path];
NSLog(#"filepath %#",tempFilePath);
//if you want only file name
NSArray *ar = [tempFilePath componentsSeparatedByString:#"/"];
NSString *filename = [[ar lastObject] uppercaseString];
NSLog(#"filename %#",filename);
}
Let me know if you have any issues
I know this thread is super old but i thought if someone finds it, here is a working answer in swift:
func fileName(for infoDict: [String : Any]) -> String? {
guard let referenceURL = infoDict[UIImagePickerControllerReferenceURL] as? URL else { return nil }
let result = PHAsset.fetchAssets(withALAssetURLs: [referenceURL], options: nil)
guard let asset = result.firstObject else { return nil }
let firstResource = PHAssetResource.assetResources(for: asset).first
return firstResource?.originalFilename ?? asset.value(forKey: "filename") as? String
}
Swift 4 or later
Working on My Side for the captured image
Use :
imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]){
let fileName = FileInfo.getMediaName(info: info)
}
class : pleas put the method info[:]
class FileInfo {
// Return Media name
static func getMediaName(info:[String : Any]) -> String {
if let asset = info[UIImagePickerControllerPHAsset] as? PHAsset {
let assetResources = PHAssetResource.assetResources(for: asset)
let firstObj = assetResources.first?.originalFilename as! NSString
print(firstObj)
return firstObj
}
}
}
// IMG_02256.jpeg
I am currently using AVCaptureStillImageOutput to get a full resolution picture. I am also able to get the exif metadata using the following code:
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error)
{
CFDictionaryRef metaDict = CMCopyDictionaryOfAttachments(NULL, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate);
CFMutableDictionaryRef mutableDict = CFDictionaryCreateMutableCopy(NULL, 0, metaDict);
NSLog(#"test attachments %#", mutableDict);
// set the dictionary back to the buffer
CMSetAttachments(imageSampleBuffer, mutableDict, kCMAttachmentMode_ShouldPropagate);
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
UIImage *image = [[UIImage alloc] initWithData:imageData];
[self.delegate frameReadyToSave:image withExifAttachments: mutableDict];
}];
The metadata being located in the mutableDict variable. Now, I want to save this picture in two different places, with the metadata. I want to save it on the disk in the application folders and in the Photo Library.
Now, I tried to save the image, in another method, using the following (the image variable you see is a custom object):
NSData* imageData = UIImageJPEGRepresentation(image.image, 1.0f);
[imageData writeToFile:image.filePath atomically:YES];
UIImageWriteToSavedPhotosAlbum(image.image, nil, nil, nil);
Now, the image is properly saved but does not contain any Exif metadata.
From what I have read, I need to use the PHPhotoLibrary to do so but the documentation isn't too loquacious on that. Here's what I found:
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetChangeRequest *createAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image.image];
} completionHandler:nil];
But how do I save the metadata with it?
I would suggest you use ImageIO to accomplish that:
-(void)frameReadyToSave:(UIImage*)image withExifAttachments:(NSMutableDictionary*)mutableDict
{
NSData* imageData = UIImageJPEGRepresentation(image, 1.0f);
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) imageData, NULL);
__block NSURL* tmpURL = [NSURL fileURLWithPath:#"example.jpg"]; //modify to your needs
CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef) tmpURL, kUTTypeJPEG, 1, NULL);
CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef) mutableDict);
CGImageDestinationFinalize(destination);
CFRelease(source);
CFRelease(destination);
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
[PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL:tmpURL];
} completionHandler:^(BOOL success, NSError *error) {
//cleanup the tmp file after import, if needed
}];
}
Use this to merge metadata into image data and save it to Photo Library:
func saveImageData(data: Data, metadata: NSDictionary? = nil, album: PHAssetCollection, completion:((PHAsset?)->())? = nil) {
var placeholder: PHObjectPlaceholder?
PHPhotoLibrary.shared().performChanges({
var changeRequest: PHAssetChangeRequest
if let metadata = metadata {
let newImageData = UIImage.mergeImageData(imageData: data, with: metadata)
changeRequest = PHAssetCreationRequest.forAsset()
(changeRequest as! PHAssetCreationRequest).addResource(with: .photo, data: newImageData as Data, options: nil)
}
else {
changeRequest = PHAssetChangeRequest.creationRequestForAsset(from: UIImage(data: data)!)
}
guard let albumChangeRequest = PHAssetCollectionChangeRequest(for: album),
let photoPlaceholder = changeRequest.placeholderForCreatedAsset else { return }
placeholder = photoPlaceholder
let fastEnumeration = NSArray(array: [photoPlaceholder] as [PHObjectPlaceholder])
albumChangeRequest.addAssets(fastEnumeration)
}, completionHandler: { success, error in
guard let placeholder = placeholder else {
completion?(nil)
return
}
if success {
let assets:PHFetchResult<PHAsset> = PHAsset.fetchAssets(withLocalIdentifiers: [placeholder.localIdentifier], options: nil)
let asset:PHAsset? = assets.firstObject
completion?(asset)
}
else {
completion?(nil)
}
})
}
func mergeImageData(imageData: Data, with metadata: NSDictionary) -> Data {
let source: CGImageSource = CGImageSourceCreateWithData(imageData as NSData, nil)!
let UTI: CFString = CGImageSourceGetType(source)!
let newImageData = NSMutableData()
let cgImage = UIImage(data: imageData)!.cgImage
let imageDestination: CGImageDestination = CGImageDestinationCreateWithData((newImageData as CFMutableData), UTI, 1, nil)!
CGImageDestinationAddImage(imageDestination, cgImage!, metadata as CFDictionary)
CGImageDestinationFinalize(imageDestination)
return newImageData as Data
}
I have a class that stores information about the assets on the phone (images, videos).
My class has the ResourceURLString defined as such
#property NSURL *ResourceURL;
I am setting the property while looping trough the assets on the phone as such
Item.ResourceURLString = [[asset valueForProperty:ALAssetPropertyURLs] objectForKey:[[asset valueForProperty:ALAssetPropertyRepresentations] objectAtIndex:0]];
When the user clicks on an image I want to load the image.
The code that I have is this
NSData *imageUrl = [NSData dataWithContentsOfURL:[NSURL URLWithString:[CurrentItem.ResourceURL absoluteString]]];
Img = [UIImage imageWithData:imageUrl];
But the Image is always nil
I have verified that the ResourceURL property contains the URL
assets: library://asset/asset.JPG?id=82690321-91C1-4650-8348-F3FD93D14613&ext=JPG
You can't load images in this way.
You need to use ALAssetsLibrary class for this.
Add assetslibrary framework to your project and add header files.
Use the below code for loading image:
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
ALAssetRepresentation *rep = [myasset defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
if (iref) {
UIImage *largeimage = [UIImage imageWithCGImage:iref];
yourImageView.image = largeImage;
}
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
NSLog(#"Can't get image - %#",[myerror localizedDescription]);
};
NSURL *asseturl = [NSURL URLWithString:yourURL];
ALAssetsLibrary* assetslibrary = [[[ALAssetsLibrary alloc] init] autorelease];
[assetslibrary assetForURL:asseturl
resultBlock:resultblock
failureBlock:failureblock];
Since iOS 8 you can use the Photos Framework here is how to do it in Swift 3
import Photos // use the Photos Framework
// declare your asset url
let assetUrl = URL(string: "assets-library://asset/asset.JPG?id=9F983DBA-EC35-42B8-8773-B597CF782EDD&ext=JPG")!
// retrieve the list of matching results for your asset url
let fetchResult = PHAsset.fetchAssets(withALAssetURLs: [assetUrl], options: nil)
if let photo = fetchResult.firstObject {
// retrieve the image for the first result
PHImageManager.default().requestImage(for: photo, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFill, options: nil) {
image, info in
let myImage = image //here is the image
}
}
Use PHImageManagerMaximumSize if you want to retrieve the original size of the picture. But if you want to retrieve a smaller or specific size you can replace PHImageManagerMaximumSize by CGSize(width:150, height:150)
As of iOS 9.0 ALAssetsLibraryis deprecated. Since iOS 8.0, this works with the PHPhotoLibrary. This is a small UIImage extension, Swift 2X.
This uses a fixed image size.
import Photos
extension UIImageView {
func imageFromAssetURL(assetURL: NSURL) {
let asset = PHAsset.fetchAssetsWithALAssetURLs([assetURL], options: nil)
guard let result = asset.firstObject where result is PHAsset else {
return
}
let imageManager = PHImageManager.defaultManager()
imageManager.requestImageForAsset(result as! PHAsset, targetSize: CGSize(width: 200, height: 200), contentMode: PHImageContentMode.AspectFill, options: nil) { (image, dict) -> Void in
if let image = image {
self.image = image
}
}
}
}
Getting the imageReferenceURL from the UIImagePickerController delegate:
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
imageURL = info[UIImagePickerControllerReferenceURL] as? NSURL
}
Setting the image
let imageView = UIImageView()
imageView.imageFromAssetURL(imageURL)
There might be effects I haven't encountered yet, a classic would be UITableViewCell or thread problems. I'll keep this updated, also appreciate your feedback.
For Swift 5
fetchAssets(withALAssetURLs) will be removed in a future release. Hence we using fetchAssets to get image from asset local identifier
extension UIImageView {
func imageFromLocalIdentifier(localIdentifier: String, targetSize: CGSize) {
let fetchOptions = PHFetchOptions()
// sort by date desending
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
// fetch photo with localIdentifier
let results = PHAsset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: fetchOptions)
let manager = PHImageManager.default()
results.enumerateObjects { (thisAsset, _, _) in
manager.requestImage(for: thisAsset, targetSize: targetSize, contentMode: .aspectFit, options: nil, resultHandler: {(image, _) in
DispatchQueue.main.async {[weak self] in
self?.image = image
}
})
}
}
}
Update
let image = UIImage(data: NSData(contentsOf: imageURL as URL)! as Data)
ALAsset *asset = "asset array index"
[tileView.tileImageView setImage:[UIImage imageWithCGImage:[asset thumbnail]]];