Save images in NSUserDefaults?
ATTENTION! IF YOU'RE WORKING UNDER iOS8/XCODE6 SEE MY UPDATE BELOW
For those who still looking for answer here is code of "advisable" way to save image in NSUserDefaults. You SHOULD NOT save image data directly into NSUserDefaults!
Write data:
// Get image data. Here you can use UIImagePNGRepresentation if you need transparency
NSData *imageData = UIImageJPEGRepresentation(image, 1);
// Get image path in user's folder and store file with name image_CurrentTimestamp.jpg (see documentsPathForFileName below)
NSString *imagePath = [self documentsPathForFileName:[NSString stringWithFormat:@"image_%f.jpg", [NSDate timeIntervalSinceReferenceDate]]];
// Write image data to user's folder
[imageData writeToFile:imagePath atomically:YES];
// Store path in NSUserDefaults
[[NSUserDefaults standardUserDefaults] setObject:imagePath forKey:kPLDefaultsAvatarUrl];
// Sync user defaults
[[NSUserDefaults standardUserDefaults] synchronize];
Read data:
NSString *imagePath = [[NSUserDefaults standardUserDefaults] objectForKey:kPLDefaultsAvatarUrl];
if (imagePath) {
self.avatarImageView.image = [UIImage imageWithData:[NSData dataWithContentsOfFile:imagePath]];
}
documentsPathForFileName:
- (NSString *)documentsPathForFileName:(NSString *)name {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
return [documentsPath stringByAppendingPathComponent:name];
}
For iOS8/XCODE6
As tmr and DevC mentioned in comments below there is a problem with xcode6/ios8. The difference between xcode5 and xcode 6 installation process is that xcode6 changes apps UUID after each run in xcode (see hightlighted part in path: /var/mobile/Containers/Data/Application/B0D49CF5-8FBE-4F14-87AE-FA8C16A678B1/Documents/image.jpg).
So there are 2 workarounds:
- Skip that problem, as once app installed on real device it's never changes UUID (in fact it does, but it is new app)
- Save relative path to required folder (in our case to app's root)
Here is swift version of code as a bonus (with 2nd approach):
Write data:
let imageData = UIImageJPEGRepresentation(image, 1)
let relativePath = "image_\(NSDate.timeIntervalSinceReferenceDate()).jpg"
let path = self.documentsPathForFileName(relativePath)
imageData.writeToFile(path, atomically: true)
NSUserDefaults.standardUserDefaults().setObject(relativePath, forKey: "path")
NSUserDefaults.standardUserDefaults().synchronize()
Read data:
let possibleOldImagePath = NSUserDefaults.standardUserDefaults().objectForKey("path") as String?
if let oldImagePath = possibleOldImagePath {
let oldFullPath = self.documentsPathForFileName(oldImagePath)
let oldImageData = NSData(contentsOfFile: oldFullPath)
// here is your saved image:
let oldImage = UIImage(data: oldImageData)
}
documentsPathForFileName:
func documentsPathForFileName(name: String) -> String {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true);
let path = paths[0] as String;
let fullPath = path.stringByAppendingPathComponent(name)
return fullPath
}
Error when trying to save image in NSUserDefaults using Swift
NSUserDefaults
isn't just a big truck you can throw anything you want onto. It's a series of tubes which only specific types.
What you can save to NSUserDefaults
:
NSData
NSString
NSNumber
NSDate
NSArray
NSDictionary
If you're trying to save anything else to NSUserDefaults
, you typically need to archive it to an NSData
object and store it (keeping in mind you'll have to unarchive it later when you need it back).
There are two ways to turn a UIImage
object into data. There are functions for creating a PNG representation of the image or a JPEG representation of the image.
For the PNG:
let imageData = UIImagePNGRepresentation(yourImage)
For the JPEG:
let imageData = UIImageJPEGRepresentation(yourImage, 1.0)
where the second argument is a CGFloat
representing the compression quality, with 0.0
being the lowest quality and 1.0
being the highest quality. Keep in mind that if you use JPEG, each time you compress and uncompress, if you're using anything but 1.0
, you're going to degrade the quality over time. PNG is lossless so you won't degrade the image.
To get the image back out of the data object, there's an init
method for UIImage
to do this:
let yourImage = UIImage(data:imageData)
This method will work no matter how you converted the UIImage
object to data.
In newer versions of Swift, the functions have been renamed, and reorganized, and are now invoked as:
For the PNG:
let imageData = yourImage.pngData()
For the JPEG:
let imageData = yourImage.jpegData(compressionQuality: 1.0)
Although Xcode will autocorrect the old versions for you
how to save UIImage to UserDefaults in swift 5
Try to use PropertyListEncoder() and Decoder like this:
func saveImage() {
guard let data = UIImage(named: "image").jpegData(compressionQuality: 0.5) else { return }
let encoded = try! PropertyListEncoder().encode(data)
UserDefaults.standard.set(encoded, forKey: "KEY")
}
func loadImage() {
guard let data = UserDefaults.standard.data(forKey: "KEY") else { return }
let decoded = try! PropertyListDecoder().decode(Data.self, from: data)
let image = UIImage(data: decoded)
}
Save UIImage array in NSUserDefaults
You can easily save your image in documentDirectory as below:
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let fileURL = documentDirectory?.appendingPathComponent(yourImageName)
and then, you can give your image to UserDefaults
What's the disadvantage of saving image to NSUserDefault?
NSUserDefaults
is explicitly meant for storing user defaults – images don't belong there especially because NSUserDefaults writes information into a .plist file. Raw image data does not belong into .plist files. Apart from that, it is also not very efficient: the more information you store in a .plist file, the longer it takes to load it. If you want to load a specific image, you would have to load the entire .plist file – including all the other images you don't need at the moment. Briefly speaking: don't do it.
A more appropriate and also more efficient approach for storing your images is to store them in your application's documents directory – where they belong to.
You could do that like this:
Storing your image
func saveImage(image: UIImage, fileName: String) {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let path = documentsURL?.appendingPathComponent(fileName)
do {
try UIImagePNGRepresentation(image)?.write(to: path!)
} catch {
// Handle possible errors here
print(error.localizedDescription)
}
}
Calling your saveImage
function
let theImage = UIImage(named: "yourImage")
saveImage(image: theImage!, fileName: "yourImageName")
After storing your images to your application's documents directory, you could take your image's file path and store it in your CoreData database for retrieving the image later.
CoreData can also automate this for you with a little trick. By enabling the "Allows External Storage" checkbox for your binary data attribute, CoreData will store the image somewhere on your device and will only keep a reference to the image in your database (rather than the actual image), in order to keep the performance high. Here's some more information about that.
Changing UIImageView image and save it using NSUserDefaults
You can save the selected image in a couple of ways. One is to save the name of the image in the user defaults either when the view is exiting or when the image is changed by the user.
Here is how to save it when the user changes the image:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var backgroundImage: UIImageView!
let kBGImageNameKey = "bgImageName"
let lightBG = UIImage(named: "bg1.jpg")
let darkBG = UIImage(named: "bg2.jpg")
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let defaults = NSUserDefaults.standardUserDefaults()
if let name = defaults.stringForKey(kBGImageNameKey) {
if (name == "bg1.jpg") {
backgroundImage.image = lightBG
} else {
backgroundImage.image = darkBG
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBAction func changeBG(sender: AnyObject) {
let defaults = NSUserDefaults.standardUserDefaults()
if(backgroundImage.image == lightBG){
backgroundImage.image = darkBG
defaults.setObject("bg2.jpg", forKey: kBGImageNameKey)
}
else{
backgroundImage.image = lightBG
defaults.setObject("bg1.jpg", forKey: kBGImageNameKey)
}
}
}
This is just one way of doing this just to set you in the direction.
Related Topics
How to Show Verification Code Suggestion on Keyboard from Message
How to Adjust Font Size to Fit Height and Width of Uilabel
How to Increment the Filename If File Already Exists
Disabling Auto-Play in Full Screen on Ios
Performselector May Cause a Leak Because Its Selector Is Unknown
How to Exit Iphone Application
How to Remove Constraints Programmatically That Is Added from Storyboard
Create Tap-Able "Links" in the Nsattributedstring of a Uilabel
Ios Swift - Objective C Code Migration to Swift
How to Programmatically Send Sms on the Iphone
Vertically Align Text to Top Within a Uilabel
How to Make a Uitextfield Move Up When the Keyboard Is Present - on Starting to Edit
Push Notification Issue With iOS 10
How to Select Multiple Images from Uiimagepickercontroller
Iphone/Ipad: How to Make Uitextfield Readonly (But Not Disabled)