Create Jpeg from NSData - ios

I am getting images from sever using this :
- (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, NSData *data))completionBlock
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if (!error)
{
completionBlock(YES, data);
}
else
{
completionBlock(NO, nil);
}
}];
}
Which provide me NSData . Now for better performance i would like to present the image in jpeg.
I know how to convert NSData to UIImage, with UIImage *image=[UIImage imageWithData:data];
And how to create NSData from image NSData *imageData = UIImagePNGRepresentation(image);
But both are not do what i need, i would like to take that NSData i get from the server, and create out of it JPEG image . how would i do that ?
(do i have to create first a new data with jpeg ,than the image from this data? )

To convert the data you get from the server to jpeg, use:
UIImage *image = [UIImage imageWithData:serverData];
NSData *jpegData = UIImageJPEGRepresentation(image, 0.75f);

you need to probably do something like this:
CGDataProviderRef imgProvider = CGDataProviderCreateWithCFData((CFDataRef) data);
CGImageRef imgRef = CGImageCreateWithJPEGDataProvider(imgProvider, NULL, true, kCGRenderingIntentDefault);
make sure to release imgRef and imgProvider

With Swift 5 and iOS 12, you can use CGImage, CGImageDestination and CGDataProvider in order to convert some image data. The following function implementation shows how to do so:
import Foundation
import ImageIO
import MobileCoreServices
func convertImageData(_ data: Data) -> Data? {
guard let provider = CGDataProvider(data: data as CFData) else {
print("Could not create data provider from data")
return nil
}
guard let cgImage = CGImage(jpegDataProviderSource: provider, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent) else {
print("Could not create cgImage from provider")
return nil
}
let data = NSMutableData()
guard let imageDestination = CGImageDestinationCreateWithData(data as CFMutableData, kUTTypeJPEG, 1, nil) else {
print("Could not create an image destination from url")
return nil
}
let options = [
kCGImageDestinationLossyCompressionQuality: 0.75, // Compress quality (if required)
kCGImagePropertyOrientation: CGImagePropertyOrientation.right.rawValue // Rotate image to the right (if required)
] as CFDictionary
CGImageDestinationAddImage(imageDestination, cgImage, options)
let imageIsWritten = CGImageDestinationFinalize(imageDestination)
if !imageIsWritten {
print("Image could not be written")
return nil
}
return data as Data
}
Usage:
let data: Data = …
let newData = convertImageData(data)
As an alternative, you can use CGImage, CGImageDestination and CGDataProvider in order to write some JPEG image data to a given URL. The following function implementation shows how to do so:
import Foundation
import ImageIO
import MobileCoreServices
func saveImageData(_ data: Data, to fileUrl: URL) {
guard let provider = CGDataProvider(data: data as CFData) else {
print("Could not create data provider from data")
return
}
guard let cgImage = CGImage(jpegDataProviderSource: provider, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent) else {
print("Could not create cgImage from provider")
return
}
guard let imageDestination = CGImageDestinationCreateWithURL(fileUrl as CFURL, kUTTypeJPEG, 1, nil) else {
print("Could not create an image destination from url")
return
}
let options = [
kCGImageDestinationLossyCompressionQuality: 0.75, // Compress quality (if required)
kCGImagePropertyOrientation: CGImagePropertyOrientation.right.rawValue // Rotate image to the right (if required)
] as CFDictionary
CGImageDestinationAddImage(imageDestination, cgImage, options)
let imageIsWritten = CGImageDestinationFinalize(imageDestination)
if !imageIsWritten {
print("Image could not be written")
}
}
Usage:
let data: Data = …
let documentDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileUrl = documentDirectoryUrl.appendingPathComponent("image.jpeg")
saveImageData(data, to: fileUrl)
Sources:
Image I/O Programming Guide - Working with Image Destinations
Thinking about Memory: Converting UIImage to Data in Swift

Related

How to load raw image using PHPicker in iOS programmatically?

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)
}
}

Save image to documents folder using Share Extension

My goal (besides learning how to write an iOS app extension) is to allow a user to share an image using the share button from a variety of apps including Photos and automatically rename them. Lastly then I want to save the image to the "documents" folder of the app for further use.
I'm having some problems trying to get the actual didSelectPost portion working since it seems that, unlike Objective-C examples I've seen, the loadItem operation returns a NSURL instead of an UIImage. When attempting to copy the NSUrl to my apps documents folder I get an error:
Error Domain=NSCocoaErrorDomain Code=260 "The file “IMG_0941.JPG”
couldn’t be opened because there is no such file."
UserInfo={NSFilePath=file:///var/mobile/Media/PhotoData/OutgoingTemp/B79263E5-9512-4317-9C5D-817D7EBEFA9A/RenderedPhoto/IMG_0941.JPG,
NSUnderlyingError=0x283f89080 {Error Domain=NSPOSIXErrorDomain Code=2
"No such file or directory"}}
This happens when I push the share button on a photo in the "photos" app, tap my extension and then press the "post" button.
I get the same error regardless if it's running in a simulator or real device.
Here's my hacked together progress so far:
override func didSelectPost() {
// This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
let inputItem = extensionContext?.inputItems.first as! NSExtensionItem
let attachment = inputItem.attachments!.first!
if attachment.hasItemConformingToTypeIdentifier(kUTTypeJPEG as String) {
attachment.loadItem(forTypeIdentifier: kUTTypeJPEG as String, options: nil) { data, error in
var image: UIImage?
if let someUrl = data as? NSURL {
do {
// a ends up being nil in both of these cases
let a = NSData(contentsOfFile: someUrl.absoluteString!)
image = UIImage(data: a as! Data)
// let a = try Data(contentsOf: someUrl)
// image = UIImage(contentsOfFile: someUrl.absoluteString)
} catch {
print(error)
}
} else if let someImage = data as? UIImage {
image = someImage
}
if let someImage = image {
guard let compressedImagePath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?.appendingPathComponent("theimage.jpg", isDirectory: false) else {
return
}
let compressedImageData = someImage.jpegData(compressionQuality: 1)
guard (try? compressedImageData?.write(to: compressedImagePath)) != nil else {
return
}
} else {
print("Bad share data")
}
}
}
// Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
Notice I'm casting the img variable as an NSURL. I've tried to cast it as a UIImage but that throws an exception.
I have some other things I'd like to do to the image, like read it's EXIF data but for now this is what I have. Any suggestions would be great as I'm really struggling to wrap my head around and learn this environment.
Similar but unsuccessful posts I've tried, notice they are all Objective-C:
iOS Share Extension issue when sharing images from Photo library
Share image using share extension in ios8
How to add my app to the share sheet action
[edit] Matched the layout of one of the better answers, still with no luck.
I have review your code and there is some mistake in the code. I have fixed it .
Replace your code with it
func share() {
let inputItem = extensionContext!.inputItems.first! as! NSExtensionItem
let attachment = inputItem.attachments!.first as! NSItemProvider
if attachment.hasItemConformingToTypeIdentifier( kUTTypeImage as String) {
attachment.loadItem(forTypeIdentifier: kUTTypeImage as String, options: [:]) { (data, error) in
var image: UIImage?
if let someURl = data as? URL {
image = UIImage(contentsOfFile: someURl.path)
}else if let someImage = data as? UIImage {
image = someImage
}
if let someImage = image {
guard let compressedImagePath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?.appendingPathComponent("shareImage.jpg", isDirectory: false) else {
return
}
let compressedImageData = UIImageJPEGRepresentation(someImage, 1)
guard (try? compressedImageData?.write(to: compressedImagePath)) != nil else {
return
}
}else{
print("bad share data")
}
}
}
}
I have the same issue. The solution I was able to implement:
Get URL to image. This URL is useless because I got 260 error when try to load image using this URL. Interesting that this comes after some recent updates because it works before
Get file name with extension from this URL
Iterate over all images in user's photo library and find the image name == name from ULR
Extract the image data
- (void)didSelectPost {
for (NSItemProvider* itemProvider in ((NSExtensionItem*)self.extensionContext.inputItems[0]).attachments ) {
// get type of file extention (jpeg, file, url, png ...)
NSArray *registeredTypeIdentifiers = itemProvider.registeredTypeIdentifiers;
if ([itemProvider hasItemConformingToTypeIdentifier:registeredTypeIdentifiers.firstObject]) {
[itemProvider loadItemForTypeIdentifier:registeredTypeIdentifiers.firstObject options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {
NSData *imgData;
NSString* imgPath = ((NSURL*) item).absoluteString;
if(imgPath == nil)
imgPath = [NSString stringWithFormat:#"%#", item];
NSCharacterSet* set = [NSCharacterSet URLHostAllowedCharacterSet];
NSString* imgPathEscaped = [imgPath stringByAddingPercentEncodingWithAllowedCharacters:set];
NSString* fileName = [imgPath lastPathComponent];
NSError* error2 = nil;
//try load from file path
__block NSData* data2 = [NSData dataWithContentsOfFile:imgPath options: NSDataReadingUncached error:&error2];
if(data2 == nil) //try load as URL
data2 = [NSData dataWithContentsOfURL:[NSURL URLWithString:imgPath] options: NSDataReadingUncached error:&error2];
if(data2 == nil) //all failed so try hacky way
{
NSString* searchFilename = [fileName lowercaseString];
PHFetchResult *results = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:nil];
[results enumerateObjectsUsingBlock:^(PHAsset *obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSArray* resources = [PHAssetResource assetResourcesForAsset:obj];
NSString* fileName2 = [NSString stringWithFormat:#"%#", ((PHAssetResource*)resources[0]).originalFilename].lowercaseString;
if ([fileName2 isEqual:searchFilename])
{
NSLog(#"found %#", fileName2);
PHImageManager* mgr = [PHImageManager defaultManager];
PHImageRequestOptions * options = [PHImageRequestOptions alloc];
options.synchronous = YES;
[mgr requestImageDataForAsset:obj options:options resultHandler:^(NSData * _Nullable imageData33, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info)
{
//imageData33 is your image
data2 = imageData33;
}];
}
}];
}
}];
}
}
// Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
[self.extensionContext completeRequestReturningItems:#[] completionHandler:nil];
}
func getPhotofolder() -> String{
let fileManager = FileManager.default
let paths = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("hsafetyPhoto")
if !fileManager.fileExists(atPath: paths){
try! fileManager.createDirectory(atPath: paths, withIntermediateDirectories: true, attributes: nil)
}else{
print("Already dictionary created.")
}
return paths
}
func saveImageDocumentDirectory(photo : UIImage, photoUrl : String) -> Bool{
let fileManager = FileManager.default
let paths = Utility.getPhotofolder().stringByAppendingPathComponent(pathComponent: photoUrl)
print("image's path \(paths)")
if !fileManager.fileExists(atPath: paths){
print("file already exits \(paths)")
let imageData = UIImageJPEGRepresentation(photo, 0.5)
fileManager.createFile(atPath: paths as String, contents: imageData, attributes: nil)
if !fileManager.fileExists(atPath: paths){
return false
}else{
return true
}
}else{
print(paths)
let imageData = UIImageJPEGRepresentation(photo, 0.5)
fileManager.createFile(atPath: paths as String, contents: imageData, attributes: nil)
if !fileManager.fileExists(atPath: paths){
return false
}else{
return true
}
}
}
func showimage(image_name : String) {
let documentsUrl = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let imgUrl = documentsUrl.appendingPathComponent(image_name)
if(FileManager.default.fileExists(atPath:imgUrl.path))
{
do {
let data = try Data(contentsOf:imgUrl)
self.imageView.image = UIImage(data:data)
}catch {
print(error)
} } else{
self.imageView.image = UIImage(named:"default.jpg") //Display any default image
}
}

Save the exif metadata using the new PHPhotoLibrary

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
}

Given a CIImage, what is the fastest way to write image data to disk?

I'm working with PhotoKit and have implemented filters users can apply to photos in their Photo Library. I am currently obtaining the image, applying a filter, returning the edited version as a CIImage, then I convert the CIImage into NSData using UIImageJPEGRepresentation so I may write that out to disk. While this works beautifully, when users attempt to edit really large (like 30 MB) photos it can take upwards of 30 seconds for this to occur, with 98% of the time spent on UIImageJPEGRepresentation (stat obtained from Instruments).
I am looking for a more efficient way to save this edited photo to disk without compromising quality, if possible.
I understand UIImagePNGRepresentation may result in improved quality, but this is even slower than the JPEG representation.
This is what I am currently doing, on the default priority thread (qos_class_utility):
func jpegRepresentationOfImage(image: CIImage) -> NSData {
let eaglContext = EAGLContext(API: .OpenGLES2)
let ciContext = CIContext(EAGLContext: eaglContext)
let outputImageRef = ciContext.createCGImage(image, fromRect: image.extent())
let uiImage = UIImage(CGImage: outputImageRef, scale: 1.0, orientation: UIImageOrientation.Up)
return UIImageJPEGRepresentation(uiImage, 0.9) //this takes upwards of 20-30 seconds with large photos!
}
//writing out to disk:
var error: NSError?
let success = jpegData.writeToURL(contentEditingOutput.renderedContentURL, options: NSDataWritingOptions.AtomicWrite, error: &error)
I would suggest passing the CGImage directly to ImageIO using CGImageDestination. You can pass a dictionary to CGImageDestinationAddImage to indicate the compression quality, image orientation, etc.
CFDataRef save_cgimage_to_jpeg (CGImageRef image)
{
CFMutableDataRef cfdata = CFDataCreateMutable(nil,0);
CGImageDestinationRef dest = CGImageDestinationCreateWithData(data, CFSTR("public.jpeg"), 1, NULL);
CGImageDestinationAddImage(dest, image, NULL);
if(!CGImageDestinationFinalize(dest))
; // error
CFRelease(dest);
return cfdata
}
Try using the CIContext method writeJPEGRepresentation (available iOS 10) as follows to eschew UIImage and make the writing faster.
extension CIImage {
#objc func saveJPEG(_ name:String, inDirectoryURL:URL? = nil, quality:CGFloat = 1.0) -> String? {
var destinationURL = inDirectoryURL
if destinationURL == nil {
destinationURL = try? FileManager.default.url(for:.documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
}
if var destinationURL = destinationURL {
destinationURL = destinationURL.appendingPathComponent(name)
if let colorSpace = CGColorSpace(name: CGColorSpace.sRGB) {
do {
let context = CIContext()
try context.writeJPEGRepresentation(of: self, to: destinationURL, colorSpace: colorSpace, options: [kCGImageDestinationLossyCompressionQuality as CIImageRepresentationOption : quality])
return destinationURL.path
} catch {
return nil
}
}
}
return nil
}
}
The #objc keyword enables you to call the method in Objective-C as:
NSString* path = [image saveJPEG:#"image.jpg" inDirectoryURL:url quality:1.0];
For PNG there is a similar method writePNGRepresentation for iOS 11:
if #available(iOS 11.0, *) {
if let colorSpace = CGColorSpace(name: CGColorSpace.sRGB) {
do {
let format = CIFormat.RGBA8
try context.writePNGRepresentation(of: self, to: destinationURL, format: format, colorSpace: colorSpace)
return destinationURL.path
} catch {
return nil
}
}
}

NSData and UIImage

I am trying to load UIImage object from NSData, and the sample code was to NSImage, I guess they should be the same. But just now loading the image, I am wondering what's the best to troubleshoot the UIImage loading NSData issue.
I didn't try UIImageJPEGRepresentation() before, but UIImagePNGRepresentation works fine for me, and conversion between NSData and UIImage is dead simple:
NSData *imageData = UIImagePNGRepresentation(image);
UIImage *image=[UIImage imageWithData:imageData];
UIImage has an -initWithData: method. From the docs: "The data in the data parameter must be formatted to match the file format of one of the system’s supported image types."
Try this to convert an image to NSdata:
UIImage *img = [UIImage imageNamed:#"image.png"];
NSData *data1 = UIImagePNGRepresentation(img);
theData should be a NSData object which already contains the data. You need to do the file loading/downloading to the NSData object before it is used. You can inspect it by using NSLog on theData and see if it contains the valid data.
For safe execution of code, use if-let block with Data, as function UIImagePNGRepresentation returns, optional value.
if let img = UIImage(named: "Hello.png") {
if let data:Data = UIImagePNGRepresentation(img) {
// Handle operations with data here...
}
}
Note: Data is Swift 3 class. Use Data instead of NSData with
Swift 3
Generic image operations (like png & jpg both):
if let img = UIImage(named: "Hello.png") {
if let data:Data = UIImagePNGRepresentation(img) {
handleOperationWithData(data: data)
} else if let data:Data = UIImageJPEGRepresentation(img, 1.0) {
handleOperationWithData(data: data)
}
}
*******
func handleOperationWithData(data: Data) {
// Handle operations with data here...
if let image = UIImage(data: data) {
// Use image...
}
}
By using extension:
extension UIImage {
var pngRepresentationData: Data? {
return UIImagePNGRepresentation(img)
}
var jpegRepresentationData: Data? {
return UIImageJPEGRepresentation(self, 1.0)
}
}
*******
if let img = UIImage(named: "Hello.png") {
if let data = img.pngRepresentationData {
handleOperationWithData(data: data)
} else if let data = jpegRepresentationData {
handleOperationWithData(data: data)
}
}
*******
func handleOperationWithData(data: Data) {
// Handle operations with data here...
if let image = UIImage(data: data) {
// Use image...
}
}

Resources