【问题标题】:Save images in NSUserDefaults?在 NSUserDefaults 中保存图像?
【发布时间】:2011-10-02 16:10:50
【问题描述】:

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

【问题讨论】:

    标签: ios objective-c iphone swift ipad


    【解决方案1】:

    在 NSUserDefaults 中保存图像:

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

    从 NSUserDefaults 中检索图像:

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

    【讨论】:

    • 技术上正确,绝对不推荐。 NSUserDefaults 输出到 plist,这显然不是原始图像数据的地方。
    • @cdstamper 我读到 plist 这些天在 mac osx 上以二进制格式存储。如果在扩展和包含应用程序之间使用 IPC(文件可能会或可能不会在两者之间使用)。那么有什么问题呢?
    • 这个问题针对 iPhone/iPad。无论如何,除非 Apple 的文档另有建议,否则 NSUserDefaults 用于小型应用程序偏好而不是存储。正如指定的 (developer.apple.com/Library/mac/documentation/Cocoa/Reference/…),“NSUserDefaults 类提供了方便的方法来访问浮点、双精度、整数、布尔值和 URL 等常见类型”
    • @cdstamper 那么存储用户登录的应用程序的个人资料图像的推荐方式是什么?我不想只使用 CoreData 来存储 1 张图像,而且我每次需要显示图像时都想访问云存储/数据库,这只是昂贵/不必要的。
    • @annjawn 只需将其写入文件系统,并将 URL 存储在 NSUserDefaults 中
    【解决方案2】:

    是的,您可以使用。但由于是用于存储偏好设置,所以可以更好地将图片保存到文件夹中。

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

    【讨论】:

      【解决方案3】:

      是的,在技术上是可行的

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

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            【解决方案6】:

            来自苹果文档,

            NSUserDefaults 类提供了方便的方法来访问常用类型,例如浮点数、双精度数、整数、布尔值和 URL。默认对象必须是一个属性列表,即以下实例(或集合实例的组合):NSData、NSString、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];
            

            【讨论】:

              【解决方案7】:

              注意!如果您在 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 正如下面 cmets 中提到的 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
              }
              

              【讨论】:

              • 代码第一次对我不起作用,无法从磁盘检索图像。应用程序运行之间的文档文件夹路径不同。可能是绝对路径与相对路径的问题?代码正在写入一个 *imagePath,但在下一次运行应用程序时,需要一个不同的 *imagePath 来获取文件。简单的解决方案是在 nsuserdefaults 中仅保存文件名,然后在下一次运行应用程序时,重新获取文档文件夹路径,附加文件名,其余代码效果很好! (谢谢尼基塔图克)
              • 那么基本上iOS8/Xcode 6的问题如果主要在设备上运行就完全无效了?
              • 我只想补充:使用 PNGRepresentation 不会自动保存 Orientation 元数据。您必须手动执行此操作,或者只使用 JPEG 版本。
              • 将图像保存到该位置会影响 iCloud 数据 - 这意味着如果它不是用户生成的,我们不应该保存到该位置?
              • @NikitaTook 一组图像怎么样?我该怎么做呢?
              【解决方案8】:

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

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


              NSUserDefaults(不推荐)

              保存到 NSUserDefaults

              此方法允许您将任何UIImage 保存到NSUserDefaults

              -(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 加载

              此方法允许您从NSUserDefaults 加载任何UIImage

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

              这就是你的称呼:

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

              更好的选择

              保存到文档目录

              此方法允许您将任何UIImage 保存到应用内的Documents 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];
              

              从文档目录加载

              此方法允许您从应用程序的Documents Directory 加载任何UIImage

              -(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”吗??
              • 在这种情况下,我使用文档目录路径作为参数。但是,您可以在应用的沙盒/捆绑包中使用您有权访问的任何目录。
              【解决方案9】:

              对于 swift 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...
              }
              

              【讨论】:

                【解决方案10】:

                适用于 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)!
                

                【讨论】:

                  【解决方案11】:

                  由于这个问题的谷歌搜索索引很高 - 这是@NikitaTook 在当今时代的答案,即 Swift 3 和 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
                      }
                  }
                  

                  【讨论】:

                    【解决方案12】:

                    对于 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?)
                    • 让 imageDefaults = UserDefaults.standard
                    【解决方案13】:

                    斯威夫特 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
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2015-02-12
                      • 2016-05-19
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多