是否在NSUserDefaults中保存图像?


Answers:


84

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

对于仍在寻找答案的用户,这里提供了“建议”方式的代码,用于将图像保存在NSUserDefaults中。您不应该将图像数据直接保存到NSUserDefaults中!

写入数据:

// 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];

读取数据:

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];
}

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

因此,有两种解决方法:

  1. 跳过该问题,因为一旦将应用程序安装在真实设备上,它就永远不会更改UUID(事实上,它确实可以更改,但是它是新应用程序)
  2. 将相对路径保存到所需文件夹(在本例中为应用程序的根目录)

这是快速版本的代码,作为奖励(使用第二种方法):

写入数据:

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

读取数据:

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
}

2
代码第一次对我不起作用,无法从磁盘检索图像。应用程序运行之间文档文件夹的路径不同。可能是绝对路径还是相对路径的问题?该代码正在写入一个* imagePath,但是在下一轮应用中,需要另一个* imagePath来获取文件。一个简单的解决方案是仅将文件名保存在nsuserdefaults中,然后在下次运行应用程序时,重新获取文档文件夹路径,追加文件名以及其余代码,效果很好!(感谢Nikita Took)
tmr 2014年

因此,如果iOS8 / Xcode 6问题主要在设备上运行,那么从本质上讲是完全无效的吗?
micnguyen 2014年

我只想添加:使用PNGRepresentation不会自动保存Orientation元数据。您将必须手动执行此操作,或者仅使用JPEG版本。
micnguyen 2014年

将图像保存到该位置是否会影响iCloud数据-如果不是由用户生成的图像,我们不应该保存到该位置吗?
克里斯·哈里森

1
@NikitaTook一系列图像呢?我将如何去做?
GJZ '16

138

要将图像保存在NSUserDefaults中:

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

要从NSUserDefaults检索图像:

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

17
技术上正确,绝对不推荐。NSUserDefaults输出到plist,这显然不是原始图像数据的地方。
cdstamper 2014年

@cdstamper我读到这些天plist以二进制格式存储在mac osx上。如果在扩展名和包含应用程序的IPC之间使用,则使用IPC(文件之间可以使用也可以不使用)。所以有什么问题?
托德2014年

这个问题针对iPhone / iPad。无论如何,除非Apple的文档另有建议,否则NSUserDefaults适用于小型应用程序首选项而不是存储。按照指定的说明(developer.apple.com/Library/mac/documentation/Cocoa/Reference/…),“ NSUserDefaults类提供了用于访问常见类型(例如浮点数,双精度数,整数,布尔值和URL)的便捷方法”
cdstamper

@cdstamper,那么,对于用户登录的应用程序,推荐的存储方式是什么(例如)?我不想仅使用CoreData来存储1张图像,我感觉每次需要显示图像时,都需要访问云存储/数据库,这是昂贵的/不必要的。
AnBisw

2
@annjawn只是将其写入文件系统,并将URL存储在NSUserDefaults中
cdstamper

29

尽管可以保存UIImageNSUserDefaults,但通常不建议这样做,因为它不是保存图像的最有效方法。一种更有效的方法是将图像保存在应用程序的中Documents Directory

出于此问题的目的,我已将答案附加到您的问题上,并提供了一种更有效的保存方法UIImage


NSUserDefaults(不推荐)

保存到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];
}

这就是你的称呼:

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

从NSUserDefaults加载

这种方法允许你加载任何UIImageNSUserDefaults

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

这就是你的称呼:

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

更好的选择

保存到文档目录

通过此方法,您可以将任何内容保存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);
    }
}

这就是你的称呼:

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

从文档目录加载

此方法允许您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;
}

这就是你的称呼:

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

另一种选择

将UIImage保存到照片库

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

UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);

将多个UIImage保存到照片库

通过此方法,您可以将多个保存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);
    }
}

这就是你的称呼:

[self saveImagesToPhotoAlbums:images];

images你是哪里NSArray组成的UIImages


很棒的帖子!您如何确定要为输入设置的目录inDirectory:(NSString *)directoryPath-您可以将其命名为“ myAppData”吗?
Supertecnoboff

好吧,在这种情况下,我使用了Documents Directory路径作为参数。但是,您可以使用应用程序的沙箱/捆绑包中有权访问的任何目录。
Fernando Cervantes

10

对于Swift 4

我几乎尝试了这个问题的所有内容,但没有人为我工作。我找到了解决方案。首先,我为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)
    } 
}

在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")

在mainVC中:

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

imageDefaults?)
Oleksandr

let imageDefaults = UserDefaults.standard
BilalŞimşek

8

快速2.2

储藏:

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

要检索:

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

5

是的,技术上可行

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

但不建议使用,因为plist不适用于存放大量二进制数据(特别是User Prefs)的地方。最好将图像保存到用户docs文件夹,并将对该对象的引用存储为URL或路径。


4

对于Swift 3JPG格式

注册默认图像:

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

保存图片 :

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

载入图片:

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

3

从Apple文档中,

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

您可以这样保存图像:-

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

像这样阅读:

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

2

从技术上讲这是可行的,但不建议这样做。而是将映像保存到磁盘。NSUserDefaults用于小的设置,而不是大的二进制数据文件。


1

将图像保存到NSUserDefault:

NSData *imageData; 
// create NSData-object from image
imageData = UIImagePNGRepresentation([dic objectForKey:[NSString stringWithFormat:@"%d",i]]); 
// save NSData-object to UserDefaults
[[NSUserDefaults standardUserDefaults] setObject:imageData forKey:[NSString stringWithFormat:@"%d",i]];

从NSUserDefault加载图像:

NSData *imageData;
// Load NSData-object from NSUserDefault
imageData = [[NSUserDefaults standardUserDefaults] valueForKey:[NSString stringWithFormat:@"%d",i]];
// get Image from NSData
[image setObject:[UIImage imageWithData:imageData] forKey:[NSString stringWithFormat:@"%d",i]];

1

是的,您可以使用。但是由于它是用于存储首选项的,所以您可以更好地将图像保存到文档文件夹中。

NSUserDefaults如果需要,您可以在中找到路径。


1

由于此问题的Google搜索索引较高-在今天的时代,这是@NikitaTook的答案,即Swift 3和Swift 4(带有异常处理)。

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

writeFile接收jpg图像的文件名(扩展名为.jpg)及其UIImage本身,如果可以保存则返回true,否则,如果不能写图像则返回false,此时可以将图像存储在Userdefaults其中将是您的备份计划,或者只是重试一次。该readFile函数接受图像文件名并返回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
    }
}

1

Swift 4.x
Xcode 11.x

func saveImageInUserDefault(img:UIImage, key:String) {
    UserDefaults.standard.set(img.pngData(), forKey: key)
}

func getImageFromUserDefault(key:String) -> UIImage? {
    let imageData = UserDefaults.standard.object(forKey: key) as? Data
    var image: UIImage? = nil
    if let imageData = imageData {
        image = UIImage(data: imageData)
    }
    return image
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.