ios 在 NSUserDefaults 中保存图像?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6648518/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 20:41:57  来源:igfitidea点击:

Save images in NSUserDefaults?

iosobjective-ciphoneswiftipad

提问by Florik

Is it possible to save images into NSUserDefaultsas an object and then retrieve for further use?

是否可以将图像保存NSUserDefaults为对象,然后检索以供进一步使用?

回答by Bonny

To save an image in NSUserDefaults:

在 NSUserDefaults 中保存图像:

[[NSUserDefaults standardUserDefaults] setObject:UIImagePNGRepresentation(image) forKey:key];

To retrieve an image from NSUserDefaults:

从 NSUserDefaults 检索图像:

NSData* imageData = [[NSUserDefaults standardUserDefaults] objectForKey:key];
UIImage* image = [UIImage imageWithData:imageData];

回答by Nikita Took

ATTENTION! IF YOU'RE WORKING UNDER iOS8/XCODE6 SEE MY UPDATE BELOW

注意力!如果您在 iOS8/XCODE6 下工作,请参阅下面的更新

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!

对于那些仍然在这里寻找答案的人来说,这是在 NSUserDefaults 中保存图像的“建议”方式的代码。您不应该将图像数据直接保存到 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:

文件路径ForFileName:

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

    return [documentsPath stringByAppendingPathComponent:name];
}

For iOS8/XCODE6As 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 UUIDafter each run in xcode (see hightlighted part in path: /var/mobile/Containers/Data/Application/B0D49CF5-8FBE-4F14-87AE-FA8C16A678B1/Documents/image.jpg).

对于 iOS8/XCODE6正如下面评论中提到的 tmr 和 DevC,xcode6/ios8 存在问题。xcode5 和 xcode 6 安装过程的区别在于 xcode6每次在 xcode 中运行后都会更改应用程序 UUID(参见路径中突出显示的部分:/var/mobile/Containers/Data/Application/ B0D49CF5-8FBE-4F14-87AE-FA8C16A678B1/Documents/图像.jpg)。

So there are 2 workarounds:

所以有两种解决方法:

  1. Skip that problem, as once app installed on real device it's never changes UUID (in fact it does, but it is new app)
  2. Save relative path to required folder (in our case to app's root)
  1. 跳过这个问题,因为一旦应用程序安装在真实设备上,它就永远不会改变 UUID(实际上确实如此,但它是新应用程序)
  2. 保存所需文件夹的相对路径(在我们的例子中是应用程序的根目录)

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:

文件路径ForFileName:

func documentsPathForFileName(name: String) -> String {
    let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true);
    let path = paths[0] as String;
    let fullPath = path.stringByAppendingPathComponent(name)

    return fullPath
}

回答by Fernando Cervantes

While it is possible to save a UIImageto NSUserDefaults, it is often not recommended as it is not the most efficient way to save images; a more efficient way is to save your image in the application's Documents Directory.

虽然可以将 a 保存UIImageNSUserDefaults,但通常不建议这样做,因为这不是保存图像的最有效方式;更有效的方法是将图像保存在应用程序的Documents Directory.

For the purpose of this question, I have attached the answer to your question, along with the more efficient way of saving a UIImage.

出于这个问题的目的,我附上了您问题的答案,以及更有效的保存UIImage.



NSUserDefaults (Not Recommended)

NSUserDefaults(不推荐)

Saving to NSUserDefaults

保存到 NSUserDefaults

This method allows you to save any UIImageto NSUserDefaults.

此方法允许您将任何内容保存UIImageNSUserDefaults.

-(void)saveImageToUserDefaults:(UIImage *)image ofType:(NSString *)extension forKey:(NSString *)key {
    NSData * data;

    if ([[extension lowercaseString] isEqualToString:@"png"]) {
        data = UIImagePNGRepresentation(image);
    } else if ([[extension lowercaseString] isEqualToString:@"jpg"]) {
        data = UIImageJPEGRepresentation(image, 1.0);
    }

    NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults setObject:data forKey:key];
    [userDefaults synchronize];
}

This is how you call it:

你是这样称呼它的:

[self saveImageToUserDefaults:image ofType:@"jpg" forKey:@"myImage"];
[[NSUserDefaults standardUserDefaults] synchronize];


Loading From NSUserDefaults

从 NSUserDefaults 加载

This method allows you to load any UIImagefrom NSUserDefaults.

此方法允许您UIImageNSUserDefaults.

-(UIImage *)loadImageFromUserDefaultsForKey:(NSString *)key {
    NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults];
    return [UIImage imageWithData:[userDefaults objectForKey:key]];
}

This is how you call it:

你是这样称呼它的:

UIImage * image = [self loadImageFromUserDefaultsForKey:@"myImage"];


A Better Alternative

更好的选择

Saving to Documents Directory

保存到文档目录

This method allows you to save any UIImageto the Documents Directorywithin the app.

此方法允许您将任何内容保存UIImageDocuments Directory应用程序内。

-(void)saveImage:(UIImage *)image withFileName:(NSString *)imageName ofType:(NSString *)extension inDirectory:(NSString *)directoryPath {
    if ([[extension lowercaseString] isEqualToString:@"png"]) {
        [UIImagePNGRepresentation(image) writeToFile:[directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", imageName, @"png"]] options:NSAtomicWrite error:nil];
    } else if ([[extension lowercaseString] isEqualToString:@"jpg"] || [[extension lowercaseString] isEqualToString:@"jpeg"]) {
        [UIImageJPEGRepresentation(image, 1.0) writeToFile:[directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", imageName, @"jpg"]] options:NSAtomicWrite error:nil];
    } else {
        NSLog(@"Image Save Failed\nExtension: (%@) is not recognized, use (PNG/JPG)", extension);
    }
}

This is how you call it:

你是这样称呼它的:

NSString * documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
[self saveImage:image withFileName:@"Ball" ofType:@"jpg" inDirectory:documentsDirectory];


Loading From Documents Directory

从文档目录加载

This method allows you to load any UIImagefrom the application's Documents Directory.

此方法允许您UIImage从应用程序的Documents Directory.

-(UIImage *)loadImageWithFileName:(NSString *)fileName ofType:(NSString *)extension inDirectory:(NSString *)directoryPath {
    UIImage * result = [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/%@.%@", directoryPath, fileName, [extension lowercaseString]]];

    return result;
}

This is how you call it:

你是这样称呼它的:

NSString * documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
UIImage * image = [self loadImageWithFileName:@"Ball" ofType:@"jpg" inDirectory:documentsDirectory];


A Different Alternative

不同的选择

Saving UIImage to Photo Library

将 UIImage 保存到照片库

This method allows you to save any UIImageto the device's Photo Library, and is called as follows:

此方法允许您将任何内容保存UIImage到设备的Photo Library, 并按如下方式调用:

UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);

Saving multiple UIImages to Photo Library

将多个 UIImages 保存到照片库

This method allows you to save multiple UIImagesto the device's Photo Library.

此方法允许您将多个保存UIImages到设备的Photo Library.

-(void)saveImagesToPhotoAlbums:(NSArray *)images {
    for (int x = 0; x < [images count]; x++) {
        UIImage * image = [images objectAtIndex:x];

        if (image != nil) UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
    }
}

This is how you call it:

你是这样称呼它的:

[self saveImagesToPhotoAlbums:images];

Where imagesis your NSArraycomposed of UIImages.

images你在哪里NSArray组成的UIImages

回答by Bilal ?im?ek

For Swift 4

对于 Swift 4

I almost tried everything in this question but no one is worked for me. and I found my solution. first I created an extension for UserDefaults like below, then just called get and set methods.

我几乎尝试了这个问题中的所有内容,但没有人为我工作。我找到了我的解决方案。首先我为 UserDefaults 创建了一个扩展,如下所示,然后只是调用了 get 和 set 方法。

extension UserDefaults {
    func imageForKey(key: String) -> UIImage? {
        var image: UIImage?
        if let imageData = data(forKey: key) {
            image = NSKeyedUnarchiver.unarchiveObject(with: imageData) as? UIImage
        }
        return image
    }
    func setImage(image: UIImage?, forKey key: String) {
        var imageData: NSData?
        if let image = image {
            imageData = NSKeyedArchiver.archivedData(withRootObject: image) as NSData?
        }
        set(imageData, forKey: key)
    } 
}

to set image as background in settingsVC I used code below.

在settingsVC中将图像设置为背景我使用了下面的代码。

let croppedImage = cropImage(selectedImage, toRect: rect, viewWidth: self.view.bounds.size.width, viewHeight: self.view.bounds.size.width)

imageDefaults.setImage(image: croppedImage, forKey: "imageDefaults")

in mainVC :

在主VC中:

let bgImage = imageDefaults.imageForKey(key: "imageDefaults")!

回答by alicanbatur

For swift 2.2

对于快速 2.2

To store:

储藏:

NSUserDefaults.standardUserDefaults().setObject(UIImagePNGRepresentation(chosenImage), forKey: kKeyImage)

To retrieve:

检索:

if let imageData = NSUserDefaults.standardUserDefaults().objectForKey(kKeyImage),
            let image = UIImage(data: imageData as! NSData){
            // use your image here...
}

回答by Warren Burton

Yes , technically possible as in

是的,技术上可行,如

[[NSUserDefaults standardUserDefaults] setObject:UIImagePNGRepresentation(image) forKey:@"foo"];

[[NSUserDefaults standardUserDefaults] setObject:UIImagePNGRepresentation(image) forKey:@"foo"];

But not advisable because plists are not appropriate places for large blobs of binary data especially User Prefs. It would be better to save image to user docs folder and store the reference to that object as a URL or path.

但不可取,因为 plist 不适合存放大量二进制数据,尤其是用户偏好。最好将图像保存到用户文档文件夹并将对该对象的引用存储为 URL 或路径。

回答by Mc.Lover

For Swift 3and JPGformat

对于Swift 3JPG格式

Register Default Image :

注册默认图像:

UserDefaults.standard.register(defaults: ["key":UIImageJPEGRepresentation(image, 100)!])

Save Image :

保存图片 :

UserDefaults.standard.set(UIImageJPEGRepresentation(image, 100), forKey: "key")

Load Image :

加载图像:

let imageData = UserDefaults.standard.value(forKey: "key") as! Data
let imageFromData = UIImage(data: imageData)!

回答by Suraj K Thomas

From apple documentation,

从苹果文档,

The NSUserDefaults class provides convenience methods for accessing common types such as floats, doubles, integers, Booleans, and URLs. A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData.

NSUserDefaults 类提供了访问常见类型(例如浮点数、双精度数、整数、布尔值和 URL)的便捷方法。默认对象必须是一个属性列表,即一个实例(或集合实例的组合):NSData、NSString、NSNumber、NSDate、NSArray 或 NSDictionary。如果要存储任何其他类型的对象,通常应该将其存档以创建 NSData 的实例。

You can save image like this:-

您可以像这样保存图像:-

[[NSUserDefaults standardUserDefaults] setObject:UIImagePNGRepresentation([UIImage imageNamed:@"yourimage.gif"])forKey:@"key_for_your_image"];

And read like this:-

并像这样阅读:-

 NSData* imageData = [[NSUserDefaults standardUserDefaults]objectForKey:@"key_for_your_image"];
    UIImage* image = [UIImage imageWithData:imageData];

回答by Benjamin Mayo

It's technically possible, but it's not advisable. Save the image to disk instead. NSUserDefaults is meant for small settings, not big binary data files.

这在技术上是可行的,但不可取。将图像保存到磁盘。NSUserDefaults 适用于小型设置,而不是大型二进制数据文件。

回答by AnBisw

Since this question has a high google search index - here's @NikitaTook's answer in today's day and age i.e. Swift 3 and 4 (with exception handling).

由于这个问题的谷歌搜索索引很高 - 这是@NikitaTook 在当今时代的答案,即 Swift 3 和 4(带有异常处理)。

Note: This class is solely written to read and write images of JPG format to the filesystem. The Userdefaultsstuff should be handled outside of it.

注意:此类仅用于将 JPG 格式的图像读取和写入文件系统。这些Userdefaults东西应该在它之外处理。

writeFiletakes in the file name of your jpg image (with .jpg extension) and the UIImageitself and returns true if it is able to save or else returns false if it is unable to write the image, at which point you can store the image in Userdefaultswhich would be your backup plan or simply retry one more time. The readFilefunction takes in the image file name and returns a UIImage, if the image name passed to this function is found then it returns that image else it just returns a default placeholder image from the app's asset folder (this way you can avoid nasty crashes or other weird behaviors).

writeFile接受您的 jpg 图像的文件名(带有 .jpg 扩展名)和它UIImage本身,如果能够保存则返回 true,否则如果无法写入图像则返回 false,此时您可以将图像存储在Userdefaults其中将是您的备用计划,或者只是再试一次。该readFile函数接受图像文件名并返回 a UIImage,如果找到传递给此函数的图像名称,则返回该图像,否则它只返回应用程序资产文件夹中的默认占位符图像(这样您可以避免令人讨厌的崩溃或其他奇怪的行为)。

import Foundation
import UIKit

class ReadWriteFileFS{

    func writeFile(_ image: UIImage, _ imgName: String) -> Bool{
        let imageData = UIImageJPEGRepresentation(image, 1)
        let relativePath = imgName
        let path = self.documentsPathForFileName(name: relativePath)

        do {
            try imageData?.write(to: path, options: .atomic)
        } catch {
            return false
        }
        return true
    }

    func readFile(_ name: String) -> UIImage{
        let fullPath = self.documentsPathForFileName(name: name)
        var image = UIImage()

        if FileManager.default.fileExists(atPath: fullPath.path){
            image = UIImage(contentsOfFile: fullPath.path)!
        }else{
            image = UIImage(named: "user")!  //a default place holder image from apps asset folder
        }
        return image
    }
}

extension ReadWriteFileFS{
    func documentsPathForFileName(name: String) -> URL {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        let path = paths[0]
        let fullPath = path.appendingPathComponent(name)
        return fullPath
    }
}