Lưu hình ảnh trong NSUserDefaults?


80

Có thể lưu hình ảnh thành NSUserDefaultsmột đối tượng và sau đó truy xuất để sử dụng tiếp không?

Câu trả lời:


84

CHÚ Ý! NẾU BẠN ĐANG LÀM VIỆC DƯỚI iOS8 / XCODE6, XEM BẢN CẬP NHẬT CỦA TÔI BÊN DƯỚI

Đối với những người vẫn đang tìm kiếm câu trả lời, đây là mã của cách "khuyến khích" để lưu hình ảnh trong NSUserDefaults. Bạn KHÔNG NÊN lưu dữ liệu hình ảnh trực tiếp vào NSUserDefaults!

Ghi dữ liệu:

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

Đọc dữ liệu:

NSString *imagePath = [[NSUserDefaults standardUserDefaults] objectForKey:kPLDefaultsAvatarUrl];
if (imagePath) {
    self.avatarImageView.image = [UIImage imageWithData:[NSData dataWithContentsOfFile:imagePath]];
}

documentPathForFileName:

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

    return [documentsPath stringByAppendingPathComponent:name];
}

Đối với iOS8 / XCODE6 Như tmr và DevC đã đề cập trong nhận xét bên dưới, có vấn đề với xcode6 / ios8. Sự khác biệt giữa quá trình cài đặt xcode5 và xcode 6 là xcode6 thay đổi UUID ứng dụng sau mỗi lần chạy trong xcode (xem phần được tô sáng trong đường dẫn: / var / mobile / Containers / Data / Application / B0D49CF5-8FBE-4F14-87AE-FA8C16A678B1 / Documents / image.jpg).

Vì vậy, có 2 cách giải quyết:

  1. Bỏ qua vấn đề đó, vì một khi ứng dụng được cài đặt trên thiết bị thực, nó sẽ không bao giờ thay đổi UUID (thực tế là có, nhưng đó là ứng dụng mới)
  2. Lưu đường dẫn tương đối vào thư mục bắt buộc (trong trường hợp của chúng tôi là thư mục gốc của ứng dụng)

Đây là phiên bản mã nhanh như một phần thưởng (với cách tiếp cận thứ 2):

Ghi dữ liệu:

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

Đọc dữ liệu:

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

documentPathForFileName:

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
mã không hoạt động cho tôi lần đầu tiên, không thể truy xuất hình ảnh từ đĩa. đường dẫn đến thư mục tài liệu khác nhau giữa các lần chạy ứng dụng. có thể là một vấn đề của các đường dẫn tuyệt đối so với tương đối? mã đang ghi vào một * imagePath, nhưng trong lần chạy ứng dụng tiếp theo, cần một * imagePath khác để lấy tệp. giải pháp đơn giản là chỉ lưu tên tệp trong nsuserdefaults và trong lần chạy ứng dụng tiếp theo, lấy lại đường dẫn thư mục tài liệu, nối tên tệp và phần còn lại của mã đã hoạt động tốt! (cảm ơn bạn Nikita Took)
tmr

Vậy thực chất vấn đề iOS8 / Xcode 6 hoàn toàn vô hiệu nếu chủ yếu chạy trên thiết bị?
micnguyen 13/10/14

Tôi chỉ muốn nói thêm: Sử dụng PNGRepresentation sẽ không tự động lưu siêu dữ liệu Định hướng. Bạn sẽ phải làm điều đó theo cách thủ công hoặc chỉ sử dụng phiên bản JPEG.
micnguyen 14/10/14

Việc lưu hình ảnh vào vị trí đó có ảnh hưởng đến dữ liệu iCloud không - có nghĩa là nếu nó không phải do người dùng tạo, chúng ta không nên lưu vào vị trí đó?
Chris Harrison

1
@NikitaTook Còn một mảng hình ảnh thì sao? Làm thế nào tôi sẽ làm điều đó?
GJZ

138

Để lưu hình ảnh trong NSUserDefaults:

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

Để truy xuất hình ảnh từ NSUserDefaults:

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

17
Đúng kỹ thuật, tích cực không khuyến khích. NSUserDefaults xuất ra một plist, rõ ràng KHÔNG phải là nơi chứa dữ liệu hình ảnh thô.
cdstamper

@cdstamper Tôi đã đọc thấy một bảng xếp hạng được lưu trữ ở định dạng nhị phân những ngày này trên mac osx. Nếu được sử dụng giữa tiện ích mở rộng và ứng dụng chứa thì IPC được sử dụng (một tệp có thể được sử dụng ở giữa hoặc không). Vậy vấn đề là gì?
Todd

Câu hỏi này đề cập đến iPhone / iPad. Bất kể, cho đến khi tài liệu của Apple khuyến nghị khác, NSUserDefaults dành cho các tùy chọn ứng dụng nhỏ chứ không phải bộ nhớ. Như đã chỉ định ( developer.apple.com/Library/mac/documentation/Cocoa/Reference/… ), "Lớp NSUserDefaults cung cấp các phương thức thuận tiện để truy cập các loại phổ biến như float, double, integer, Booleans và URL"
cdstamper

@cdstamper, vậy cách lưu trữ được đề xuất, chẳng hạn, hình ảnh hồ sơ cho một ứng dụng mà người dùng đã đăng nhập là gì? Tôi không muốn sử dụng CoreData chỉ để lưu trữ 1 hình ảnh và tôi cảm thấy việc sử dụng bộ lưu trữ đám mây / db mỗi khi tôi cần hiển thị hình ảnh chỉ là tốn kém / không cần thiết.
AnBisw 27/09/17

2
@annjawn chỉ cần viết nó vào hệ thống tập tin, và lưu trữ các URL trong NSUserDefaults
cdstamper

29

Mặc dù có thể lưu một UIImageđến NSUserDefaults, nhưng nó thường không được khuyến khích vì nó không phải là cách hiệu quả nhất để lưu hình ảnh; một cách hiệu quả hơn là lưu hình ảnh của bạn trong ứng dụng Documents Directory.

Với mục đích của câu hỏi này, tôi đã đính kèm câu trả lời cho câu hỏi của bạn, cùng với cách tiết kiệm hiệu quả hơn a UIImage.


NSUserDefaults (Không được khuyến nghị)

Lưu vào NSUserDefaults

Phương pháp này cho phép bạn lưu bất kỳ UIImagevào 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];
}

Đây là cách bạn gọi nó:

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

Đang tải từ NSUserDefaults

Phương pháp này cho phép bạn tải bất kỳ UIImagetừ nào NSUserDefaults.

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

Đây là cách bạn gọi nó:

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

Một giải pháp thay thế tốt hơn

Lưu vào Thư mục Tài liệu

Phương pháp này cho phép bạn lưu bất kỳ UIImagevào Documents Directorybên trong ứng dụng.

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

Đây là cách bạn gọi nó:

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

Tải từ thư mục tài liệu

Phương pháp này cho phép bạn tải bất kỳ UIImagetừ ứng dụng nào Documents Directory.

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

    return result;
}

Đây là cách bạn gọi nó:

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

Một sự thay thế khác

Lưu UIImage vào Thư viện Ảnh

Phương pháp này cho phép bạn lưu bất kỳ tệp nào UIImagevào thiết bị Photo Libraryvà được gọi như sau:

UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);

Lưu nhiều ảnh UII vào Thư viện ảnh

Phương pháp này cho phép bạn lưu nhiều UIImagesvào thiết bị 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);
    }
}

Đây là cách bạn gọi nó:

[self saveImagesToPhotoAlbums:images];

Của imagesbạn NSArraybao gồm đâu UIImages.


Bài viết hay! Làm thế nào để bạn tìm ra thư mục nào để đặt cho đầu vào inDirectory:(NSString *)directoryPath- bạn có thể gọi nó bất cứ thứ gì bạn muốn như "myAppData" không ??
Supertecnoboff

Trong trường hợp này, tôi đã sử dụng đường dẫn Thư mục tài liệu làm đối số. Tuy nhiên, bạn có thể sử dụng bất kỳ thư mục nào bạn có quyền truy cập trong hộp cát / gói ứng dụng của mình.
Fernando Cervantes

11

Đối với Swift 4

Tôi gần như đã thử mọi thứ trong câu hỏi này nhưng không ai phù hợp với tôi. và tôi đã tìm ra giải pháp của mình. đầu tiên tôi tạo một phần mở rộng cho UserDefaults như bên dưới, sau đó chỉ cần gọi các phương thức get và 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)
    } 
}

để đặt hình ảnh làm nền trong cài đặt, tôi đã sử dụng mã bên dưới.

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

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

trong mainVC:

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

imageDefaults?)
Oleksandr

let imageDefaults = UserDefaults.standard
Bilal Şimşek

8

Đối với nhanh chóng 2.2

Để lưu trữ:

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

Lấy:

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

5

Có, về mặt kỹ thuật có thể như trong

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

Nhưng không nên vì plists không phải là nơi thích hợp cho các đốm màu lớn của dữ liệu nhị phân, đặc biệt là User Prefs. Sẽ tốt hơn nếu lưu hình ảnh vào thư mục tài liệu của người dùng và lưu trữ tham chiếu đến đối tượng đó dưới dạng URL hoặc đường dẫn.


4

Đối với Swift 3 và định dạng JPG

Đăng ký Hình ảnh Mặc định:

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

Lưu hình ảnh :

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

Tải hình ảnh :

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

3

Từ tài liệu của apple,

Lớp NSUserDefaults cung cấp các phương thức thuận tiện để truy cập các kiểu phổ biến như float, double, integer, Boolean và URL. Đối tượng mặc định phải là một danh sách thuộc tính, nghĩa là, một thể hiện của (hoặc đối với bộ sưu tập kết hợp các thể hiện của): NSData, NSString, NSNumber, NSDate, NSArray hoặc NSDictionary. Nếu bạn muốn lưu trữ bất kỳ loại đối tượng nào khác, thông thường bạn nên lưu trữ nó để tạo một phiên bản NSData.

Bạn có thể lưu hình ảnh như sau: -

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

Và đọc như thế này: -

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

2

Về mặt kỹ thuật thì có thể, nhưng không nên. Thay vào đó, hãy lưu hình ảnh vào đĩa. NSUserDefaults dành cho các cài đặt nhỏ, không phải tệp dữ liệu nhị phân lớn.


1

Lưu hình ảnh vào 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]];

Tải hình ảnh từ 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

Có, bạn có thể sử dụng. Nhưng vì nó là để lưu trữ các tùy chọn, bạn có thể lưu hình ảnh tốt hơn vào thư mục tài liệu.

Và bạn có thể có đường dẫn trong NSUserDefaults, nếu được yêu cầu.


1

Vì câu hỏi này có chỉ số tìm kiếm google cao - đây là câu trả lời của @ NikitaTook trong thời đại ngày nay, tức là Swift 3 và 4 (có xử lý ngoại lệ).

Lưu ý: Lớp này chỉ được viết để đọc và ghi hình ảnh có định dạng JPG vào hệ thống tệp. Những Userdefaultsthứ nên được xử lý bên ngoài nó.

writeFilelấy tên tệp của hình ảnh jpg của bạn (với phần mở rộng .jpg) và UIImagechính nó và trả về true nếu nó có thể lưu hoặc trả về false nếu nó không thể ghi hình ảnh, lúc này bạn có thể lưu trữ hình ảnh trong Userdefaultsđó sẽ là kế hoạch dự phòng của bạn hoặc đơn giản là thử lại một lần nữa. Các readFilechức năng có trong tên tập tin hình ảnh và trả về một UIImage, nếu tên hình ảnh thông qua chức năng này được tìm thấy sau đó nó sẽ trả về mà hình ảnh khác nó chỉ trả về một hình ảnh giữ chỗ mặc định từ thư mục tài sản của ứng dụng (theo cách này bạn có thể tránh tai nạn khó chịu hoặc khác hành vi kỳ lạ).

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
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.