Save An Image To Application Documents Folder From UIView On IOS
Asked Answered
L

6

117

I have a UIImageView that allows a user to place and hold an image until it can be saved. The problem is, I can't figure out how to actually save and retrieve the image I've placed in the view.

I have retrieved and placed the image in the UIImageView like this:

//Get Image 
- (void) getPicture:(id)sender {
    UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    picker.delegate = self;
    picker.allowsEditing = YES;
    picker.sourceType = (sender == myPic) ? UIImagePickerControllerSourceTypeCamera : UIImagePickerControllerSourceTypeSavedPhotosAlbum;
    [self presentModalViewController:picker animated:YES];
    [picker release];
}


- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage (UIImage *)image editingInfo:(NSDictionary *)editingInfo {
    myPic.image = image;
    [picker dismissModalViewControllerAnimated:YES];
}

It displays the selected image in my UIImageView just fine, but I have no idea how to save it. I'm saving all the other pieces of the view (mostly UITextfield) in Core Data. I've searched and searched, and tried many bits of code that people have suggested, but either I'm not entering the code correctly, or those suggestions don't work with the way I have my code set up. It's likely the former. I'd like to save the image in the UIImageView using the same action (a save button) I'm using to save the text in the UITextFields. Here's how I'm saving my UITextField info:

// Handle Save Button
- (void)save {

    // Get Info From UI
    [self.referringObject setValue:self.myInfo.text forKey:@"myInfo"];

Like I said earlier, I have tried several methods to get this to work, but can't get a grasp on it. For the first time in my life I've wanted to cause physical harm to an inanimate object, but I've managed to restrain myself.

I'd like to be able to save the image the user places into the UIImageView in the application's documents folder, and then be able to retrieve it and place it in another UIImageView for display when the user pushes that view onto the stack. Any help is greatly appreciated!

Lavatory answered 25/7, 2011 at 19:51 Comment(0)
C
344

It's all good, man. Don't harm yourself or others.

You probably don't want to store these images in Core Data, since that can impact performance if the data set grows too large. Better to write the images to files.

NSData *pngData = UIImagePNGRepresentation(image);

This pulls out PNG data of the image you've captured. From here, you can write it to a file:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
NSString *documentsPath = [paths objectAtIndex:0]; //Get the docs directory 
NSString *filePath = [documentsPath stringByAppendingPathComponent:@"image.png"]; //Add the file name
[pngData writeToFile:filePath atomically:YES]; //Write the file

Reading it later works the same way. Build the path like we just did above, then:

NSData *pngData = [NSData dataWithContentsOfFile:filePath];
UIImage *image = [UIImage imageWithData:pngData];

What you'll probably want to do is make a method that creates path strings for you, since you don't want that code littered everywhere. It might look like this:

- (NSString *)documentsPathForFileName:(NSString *)name
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);  
    NSString *documentsPath = [paths objectAtIndex:0];

    return [documentsPath stringByAppendingPathComponent:name]; 
}

Hope that's helpful.

Condensate answered 25/7, 2011 at 20:41 Comment(3)
totally correct - just wanna mention the Apple Storage Guidelines so DEPENDING on the nature of the images, it should be stored under CachesOrnery
I followed your suggestion & code. but it is not appearing into the photos sections. How did this happen?Ry
@DaniloCampos How to create a folder inside Documents Directory and then save the file inside that folder?Placable
T
4

Swift 3.0 version

let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
        
let img = UIImage(named: "1.jpg")!// Or use whatever way to get the UIImage object
let imgPath = URL(fileURLWithPath: documentDirectoryPath.appendingPathComponent("1.jpg"))// Change extension if you want to save as PNG

do{
    try UIImageJPEGRepresentation(img, 1.0)?.write(to: imgPath, options: .atomic)//Use UIImagePNGRepresentation if you want to save as PNG
}catch let error{
    print(error.localizedDescription)
}
Teth answered 23/8, 2017 at 18:9 Comment(0)
D
3

Swift 4 with extension

extension UIImage{

func saveImage(inDir:FileManager.SearchPathDirectory,name:String){
    guard let documentDirectoryPath = FileManager.default.urls(for: inDir, in: .userDomainMask).first else {
        return
    }
    let img = UIImage(named: "\(name).jpg")!

    // Change extension if you want to save as PNG.
    let imgPath = URL(fileURLWithPath: documentDirectoryPath.appendingPathComponent("\(name).jpg").absoluteString)
    do {
        try UIImageJPEGRepresentation(img, 0.5)?.write(to: imgPath, options: .atomic)
    } catch {
        print(error.localizedDescription)
    }
  }
}

Usage example

 image.saveImage(inDir: .documentDirectory, name: "pic")
Disharmony answered 19/6, 2018 at 7:26 Comment(0)
U
2

This is Fangming Ning's answer for Swift 4.2, updated with a recommended and more Swifty method for retrieving the document directory path and with better documentation. Credits to Fangming Ning for the new method as well.

guard let documentDirectoryPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
    return
}

//Using force unwrapping here because we're sure "1.jpg" exists. Remember, this is just an example.
let img = UIImage(named: "1.jpg")!

// Change extension if you want to save as PNG.
let imgPath = documentDirectoryPath.appendingPathComponent("1.jpg")

do {
    //Use .pngData() if you want to save as PNG.
    //.atomic is just an example here, check out other writing options as well. (see the link under this example)
    //(atomic writes data to a temporary file first and sending that file to its final destination)
    try img.jpegData(compressionQuality: 1)?.write(to: imgPath, options: .atomic)
} catch {
    print(error.localizedDescription)
}

Check out all the possible Data writing options here.

Unstrained answered 3/5, 2018 at 11:47 Comment(2)
Is this correct? Over in an answer to another question here I found that fileURLWithPath together with absoluteString is wrong.Rajasthan
@Rajasthan Thank you for the information, I've updated my answer and rewrote the code for Swift 4.2 as well.Retorsion
E
2
#pragma mark - Save Image To Local Directory

- (void)saveImageToDocumentDirectoryWithImage:(UIImage *)capturedImage {
    NSError *error;
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
    NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"/images"];
    
    //Create a folder inside Document Directory
    if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath])
        [[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:&error]; //Create folder

    NSString *imageName = [NSString stringWithFormat:@"%@/img_%@.png", dataPath, [self getRandomNumber]] ;
    // save the file
    if ([[NSFileManager defaultManager] fileExistsAtPath:imageName]) {
        // delete if exist
        [[NSFileManager defaultManager] removeItemAtPath:imageName error:nil];
    }
    
    NSData *imageDate = [NSData dataWithData:UIImagePNGRepresentation(capturedImage)];
    [imageDate writeToFile: imageName atomically: YES];
}


#pragma mark - Generate Random Number

- (NSString *)getRandomNumber {
    NSTimeInterval time = ([[NSDate date] timeIntervalSince1970]); // returned as a double
    long digits = (long)time; // this is the first 10 digits
    int decimalDigits = (int)(fmod(time, 1) * 1000); // this will get the 3 missing digits
    //long timestamp = (digits * 1000) + decimalDigits;
    NSString *timestampString = [NSString stringWithFormat:@"%ld%d",digits ,decimalDigits];
    return timestampString;
}
Errick answered 10/12, 2018 at 19:12 Comment(0)
C
0

In Swift:

let paths: [NSString?] = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .LocalDomainMask, true)
if let path = paths[0]?.stringByAppendingPathComponent(imageName) {
    do {
        try UIImagePNGRepresentation(image)?.writeToFile(path, options: .DataWritingAtomic)
    } catch {
        return
    }
}
Costin answered 21/8, 2016 at 9:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.