What do I need to do to save an image my program has generated (possibly from the camera, possibly not) to the system photo library on the iPhone?
This question is related to
ios
iphone
cocoa-touch
camera
uiimage
For Swift 5.0
I used this code to copy images to photo albums my application had created; When I want to copy images files I call "startSavingPhotoAlbume()" function. First I get UIImage from App folder then save it to photo albums. Because it is irrelevant I dont show how to read image from App folder.
var saveToPhotoAlbumCounter = 0
func startSavingPhotoAlbume(){
saveToPhotoAlbumCounter = 0
saveToPhotoAlbume()
}
func saveToPhotoAlbume(){
let image = loadImageFile(fileName: imagefileList[saveToPhotoAlbumCounter], folderName: folderName)
UIImageWriteToSavedPhotosAlbum(image!, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}
@objc func image(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
if (error != nil) {
print("ptoto albume savin error for \(imageFileList[saveToPhotoAlbumCounter])")
} else {
if saveToPhotoAlbumCounter < imageFileList.count - 1 {
saveToPhotoAlbumCounter += 1
saveToPhotoAlbume()
} else {
print("saveToPhotoAlbume is finished with \(saveToPhotoAlbumCounter) files")
}
}
}
homeDirectoryPath = NSHomeDirectory();
unexpandedPath = [homeDirectoryPath stringByAppendingString:@"/Pictures/"];
folderPath = [NSString pathWithComponents:[NSArray arrayWithObjects:[NSString stringWithString:[unexpandedPath stringByExpandingTildeInPath]], nil]];
unexpandedImagePath = [folderPath stringByAppendingString:@"/image.png"];
imagePath = [NSString pathWithComponents:[NSArray arrayWithObjects:[NSString stringWithString:[unexpandedImagePath stringByExpandingTildeInPath]], nil]];
if (![[NSFileManager defaultManager] fileExistsAtPath:folderPath isDirectory:NULL]) {
[[NSFileManager defaultManager] createDirectoryAtPath:folderPath attributes:nil];
}
In Swift:
// Save it to the camera roll / saved photo album
// UIImageWriteToSavedPhotosAlbum(self.myUIImageView.image, nil, nil, nil) or
UIImageWriteToSavedPhotosAlbum(self.myUIImageView.image, self, "image:didFinishSavingWithError:contextInfo:", nil)
func image(image: UIImage!, didFinishSavingWithError error: NSError!, contextInfo: AnyObject!) {
if (error != nil) {
// Something wrong happened.
} else {
// Everything is alright.
}
}
I created a UIImageView category for this, based on some of the answers above.
Header File:
@interface UIImageView (SaveImage) <UIActionSheetDelegate>
- (void)addHoldToSave;
@end
Implementation
@implementation UIImageView (SaveImage)
- (void)addHoldToSave{
UILongPressGestureRecognizer* longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
longPress.minimumPressDuration = 1.0f;
[self addGestureRecognizer:longPress];
}
- (void)handleLongPress:(UILongPressGestureRecognizer*)sender {
if (sender.state == UIGestureRecognizerStateEnded) {
UIActionSheet* _attachmentMenuSheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"Save Image", nil];
[_attachmentMenuSheet showInView:[[UIView alloc] initWithFrame:self.frame]];
}
else if (sender.state == UIGestureRecognizerStateBegan){
//Do nothing
}
}
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0) {
UIImageWriteToSavedPhotosAlbum(self.image, nil,nil, nil);
}
}
@end
Now simply call this function on your imageview:
[self.imageView addHoldToSave];
Optionally you can alter the minimumPressDuration parameter.
Swift 4
func writeImage(image: UIImage) {
UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.finishWriteImage), nil)
}
@objc private func finishWriteImage(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
if (error != nil) {
// Something wrong happened.
print("error occurred: \(String(describing: error))")
} else {
// Everything is alright.
print("saved success!")
}
}
When saving an array of photos, don't use a for loop, do the following
-(void)saveToAlbum{
[self performSelectorInBackground:@selector(startSavingToAlbum) withObject:nil];
}
-(void)startSavingToAlbum{
currentSavingIndex = 0;
UIImage* img = arrayOfPhoto[currentSavingIndex];//get your image
UIImageWriteToSavedPhotosAlbum(img, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}
- (void)image: (UIImage *) image didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo{ //can also handle error message as well
currentSavingIndex ++;
if (currentSavingIndex >= arrayOfPhoto.count) {
return; //notify the user it's done.
}
else
{
UIImage* img = arrayOfPhoto[currentSavingIndex];
UIImageWriteToSavedPhotosAlbum(img, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}
}
The simplest way is:
UIImageWriteToSavedPhotosAlbum(myUIImage, nil, nil, nil);
For Swift
, you can refer to Saving to the iOS photo library using swift
Swift 4
func writeImage(image: UIImage) {
UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.finishWriteImage), nil)
}
@objc private func finishWriteImage(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
if (error != nil) {
// Something wrong happened.
print("error occurred: \(String(describing: error))")
} else {
// Everything is alright.
print("saved success!")
}
}
For Swift 5.0
I used this code to copy images to photo albums my application had created; When I want to copy images files I call "startSavingPhotoAlbume()" function. First I get UIImage from App folder then save it to photo albums. Because it is irrelevant I dont show how to read image from App folder.
var saveToPhotoAlbumCounter = 0
func startSavingPhotoAlbume(){
saveToPhotoAlbumCounter = 0
saveToPhotoAlbume()
}
func saveToPhotoAlbume(){
let image = loadImageFile(fileName: imagefileList[saveToPhotoAlbumCounter], folderName: folderName)
UIImageWriteToSavedPhotosAlbum(image!, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}
@objc func image(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
if (error != nil) {
print("ptoto albume savin error for \(imageFileList[saveToPhotoAlbumCounter])")
} else {
if saveToPhotoAlbumCounter < imageFileList.count - 1 {
saveToPhotoAlbumCounter += 1
saveToPhotoAlbume()
} else {
print("saveToPhotoAlbume is finished with \(saveToPhotoAlbumCounter) files")
}
}
}
In Swift:
// Save it to the camera roll / saved photo album
// UIImageWriteToSavedPhotosAlbum(self.myUIImageView.image, nil, nil, nil) or
UIImageWriteToSavedPhotosAlbum(self.myUIImageView.image, self, "image:didFinishSavingWithError:contextInfo:", nil)
func image(image: UIImage!, didFinishSavingWithError error: NSError!, contextInfo: AnyObject!) {
if (error != nil) {
// Something wrong happened.
} else {
// Everything is alright.
}
}
One thing to remember: If you use a callback, make sure that your selector conforms to the following form:
- (void) image: (UIImage *) image didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo;
Otherwise, you'll crash with an error such as the following:
[NSInvocation setArgument:atIndex:]: index (2) out of bounds [-1, 1]
In Swift 2.2
UIImageWriteToSavedPhotosAlbum(image: UIImage, _ completionTarget: AnyObject?, _ completionSelector: Selector, _ contextInfo: UnsafeMutablePointer<Void>)
If you do not want to be notified when the image is done saving then you may pass nil in the completionTarget, completionSelector and contextInfo parameters.
Example:
UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.imageSaved(_:didFinishSavingWithError:contextInfo:)), nil)
func imageSaved(image: UIImage!, didFinishSavingWithError error: NSError?, contextInfo: AnyObject?) {
if (error != nil) {
// Something wrong happened.
} else {
// Everything is alright.
}
}
The important thing to note here is that your method that observes the image saving should have these 3 parameters else you will run into NSInvocation errors.
Hope it helps.
Deprecated in iOS 9.0.
There`s much more fast then UIImageWriteToSavedPhotosAlbum way to do it using iOS 4.0+ AssetsLibrary framework
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error){
if (error) {
// TODO: error handling
} else {
// TODO: success handling
}
}];
[library release];
my last answer will do it..
for each image you want to save, add it to a NSMutableArray
//in the .h file put:
NSMutableArray *myPhotoArray;
///then in the .m
- (void) viewDidLoad {
myPhotoArray = [[NSMutableArray alloc]init];
}
//However Your getting images
- (void) someOtherMethod {
UIImage *someImage = [your prefered method of using this];
[myPhotoArray addObject:someImage];
}
-(void) saveMePlease {
//Loop through the array here
for (int i=0:i<[myPhotoArray count]:i++){
NSString *file = [myPhotoArray objectAtIndex:i];
NSString *path = [get the path of the image like you would in DOCS FOLDER or whatever];
NSString *imagePath = [path stringByAppendingString:file];
UIImage *image = [[[UIImage alloc] initWithContentsOfFile:imagePath]autorelease];
//Now it will do this for each photo in the array
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
}
}
homeDirectoryPath = NSHomeDirectory();
unexpandedPath = [homeDirectoryPath stringByAppendingString:@"/Pictures/"];
folderPath = [NSString pathWithComponents:[NSArray arrayWithObjects:[NSString stringWithString:[unexpandedPath stringByExpandingTildeInPath]], nil]];
unexpandedImagePath = [folderPath stringByAppendingString:@"/image.png"];
imagePath = [NSString pathWithComponents:[NSArray arrayWithObjects:[NSString stringWithString:[unexpandedImagePath stringByExpandingTildeInPath]], nil]];
if (![[NSFileManager defaultManager] fileExistsAtPath:folderPath isDirectory:NULL]) {
[[NSFileManager defaultManager] createDirectoryAtPath:folderPath attributes:nil];
}
Below function would work. You can copy from here and paste there...
-(void)savePhotoToAlbum:(UIImage*)imageToSave {
CGImageRef imageRef = imageToSave.CGImage;
NSDictionary *metadata = [NSDictionary new]; // you can add
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeImageToSavedPhotosAlbum:imageRef metadata:metadata completionBlock:^(NSURL *assetURL,NSError *error){
if(error) {
NSLog(@"Image save eror");
}
}];
}
I created a UIImageView category for this, based on some of the answers above.
Header File:
@interface UIImageView (SaveImage) <UIActionSheetDelegate>
- (void)addHoldToSave;
@end
Implementation
@implementation UIImageView (SaveImage)
- (void)addHoldToSave{
UILongPressGestureRecognizer* longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
longPress.minimumPressDuration = 1.0f;
[self addGestureRecognizer:longPress];
}
- (void)handleLongPress:(UILongPressGestureRecognizer*)sender {
if (sender.state == UIGestureRecognizerStateEnded) {
UIActionSheet* _attachmentMenuSheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"Save Image", nil];
[_attachmentMenuSheet showInView:[[UIView alloc] initWithFrame:self.frame]];
}
else if (sender.state == UIGestureRecognizerStateBegan){
//Do nothing
}
}
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0) {
UIImageWriteToSavedPhotosAlbum(self.image, nil,nil, nil);
}
}
@end
Now simply call this function on your imageview:
[self.imageView addHoldToSave];
Optionally you can alter the minimumPressDuration parameter.
You can use this
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImageWriteToSavedPhotosAlbum(img.image, nil, nil, nil);
});
Just pass the images from an array to it like so
-(void) saveMePlease {
//Loop through the array here
for (int i=0:i<[arrayOfPhotos count]:i++){
NSString *file = [arrayOfPhotos objectAtIndex:i];
NSString *path = [get the path of the image like you would in DOCS FOLDER or whatever];
NSString *imagePath = [path stringByAppendingString:file];
UIImage *image = [[[UIImage alloc] initWithContentsOfFile:imagePath]autorelease];
//Now it will do this for each photo in the array
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
}
}
Sorry for typo's kinda just did this on the fly but you get the point
One thing to remember: If you use a callback, make sure that your selector conforms to the following form:
- (void) image: (UIImage *) image didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo;
Otherwise, you'll crash with an error such as the following:
[NSInvocation setArgument:atIndex:]: index (2) out of bounds [-1, 1]
When saving an array of photos, don't use a for loop, do the following
-(void)saveToAlbum{
[self performSelectorInBackground:@selector(startSavingToAlbum) withObject:nil];
}
-(void)startSavingToAlbum{
currentSavingIndex = 0;
UIImage* img = arrayOfPhoto[currentSavingIndex];//get your image
UIImageWriteToSavedPhotosAlbum(img, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}
- (void)image: (UIImage *) image didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo{ //can also handle error message as well
currentSavingIndex ++;
if (currentSavingIndex >= arrayOfPhoto.count) {
return; //notify the user it's done.
}
else
{
UIImage* img = arrayOfPhoto[currentSavingIndex];
UIImageWriteToSavedPhotosAlbum(img, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}
}
Below function would work. You can copy from here and paste there...
-(void)savePhotoToAlbum:(UIImage*)imageToSave {
CGImageRef imageRef = imageToSave.CGImage;
NSDictionary *metadata = [NSDictionary new]; // you can add
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeImageToSavedPhotosAlbum:imageRef metadata:metadata completionBlock:^(NSURL *assetURL,NSError *error){
if(error) {
NSLog(@"Image save eror");
}
}];
}
In Swift 2.2
UIImageWriteToSavedPhotosAlbum(image: UIImage, _ completionTarget: AnyObject?, _ completionSelector: Selector, _ contextInfo: UnsafeMutablePointer<Void>)
If you do not want to be notified when the image is done saving then you may pass nil in the completionTarget, completionSelector and contextInfo parameters.
Example:
UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.imageSaved(_:didFinishSavingWithError:contextInfo:)), nil)
func imageSaved(image: UIImage!, didFinishSavingWithError error: NSError?, contextInfo: AnyObject?) {
if (error != nil) {
// Something wrong happened.
} else {
// Everything is alright.
}
}
The important thing to note here is that your method that observes the image saving should have these 3 parameters else you will run into NSInvocation errors.
Hope it helps.
The simplest way is:
UIImageWriteToSavedPhotosAlbum(myUIImage, nil, nil, nil);
For Swift
, you can refer to Saving to the iOS photo library using swift
You can use this
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImageWriteToSavedPhotosAlbum(img.image, nil, nil, nil);
});
Deprecated in iOS 9.0.
There`s much more fast then UIImageWriteToSavedPhotosAlbum way to do it using iOS 4.0+ AssetsLibrary framework
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error){
if (error) {
// TODO: error handling
} else {
// TODO: success handling
}
}];
[library release];
Just pass the images from an array to it like so
-(void) saveMePlease {
//Loop through the array here
for (int i=0:i<[arrayOfPhotos count]:i++){
NSString *file = [arrayOfPhotos objectAtIndex:i];
NSString *path = [get the path of the image like you would in DOCS FOLDER or whatever];
NSString *imagePath = [path stringByAppendingString:file];
UIImage *image = [[[UIImage alloc] initWithContentsOfFile:imagePath]autorelease];
//Now it will do this for each photo in the array
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
}
}
Sorry for typo's kinda just did this on the fly but you get the point
my last answer will do it..
for each image you want to save, add it to a NSMutableArray
//in the .h file put:
NSMutableArray *myPhotoArray;
///then in the .m
- (void) viewDidLoad {
myPhotoArray = [[NSMutableArray alloc]init];
}
//However Your getting images
- (void) someOtherMethod {
UIImage *someImage = [your prefered method of using this];
[myPhotoArray addObject:someImage];
}
-(void) saveMePlease {
//Loop through the array here
for (int i=0:i<[myPhotoArray count]:i++){
NSString *file = [myPhotoArray objectAtIndex:i];
NSString *path = [get the path of the image like you would in DOCS FOLDER or whatever];
NSString *imagePath = [path stringByAppendingString:file];
UIImage *image = [[[UIImage alloc] initWithContentsOfFile:imagePath]autorelease];
//Now it will do this for each photo in the array
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
}
}
Source: Stackoverflow.com