Зберегти зображення в NSUserDefaults?


80

Чи можна зберегти зображення NSUserDefaultsяк об'єкт, а потім отримати для подальшого використання?

Відповіді:


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 змінює UUID додатків після кожного запуску в xcode (див. Виділену частину шляху: / var / mobile / Containers / Data / Application / B0D49CF5-8FBE-4F14-87AE-FA8C16A678B1 / Documents / image.jpg).

Отже, є 2 обхідних шляхи:

  1. Пропустіть цю проблему, оскільки після встановлення програми на реальному пристрої вона ніколи не змінює UUID (насправді це робиться, але це нова програма)
  2. Зберегти відносний шлях до необхідної папки (у нашому випадку до кореня програми)

Ось швидка версія коду як бонус (з 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, і при наступному запуску програми повторно отримати шлях до папки документів, додати ім'я файлу, а решта коду працювала чудово! (спасибі Микиті Взяв)
tmr

Отже, по суті, проблема iOS8 / Xcode 6 є абсолютно порожньою, якщо в основному вона працює на пристрої?
micnguyen

Я просто хочу додати: використання PNGRepresentation не збереже метадані орієнтації автоматично. Вам доведеться це робити вручну або просто використовувати версію JPEG.
micnguyen

Чи вплине збереження зображень у цьому місці на дані iCloud - тобто, якщо це не генерується користувачем, ми не повинні зберігати в цьому місці?
Кріс Гаррісон,

1
@NikitaTook А як щодо масиву зображень? Як би я це зробив?
GJZ,

138

Щоб зберегти зображення в NSUserDefaults:

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

Щоб отримати зображення з NSUserDefaults:

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

17
Технічно правильно, позитивно не рекомендується. NSUserDefaults виводить у список, який, очевидно, НЕ місце для вихідних даних зображень.
chris stamper

@cdstamper Я читав, що plist сьогодні зберігається у бінарному форматі на mac osx. Якщо використовується між розширенням і містить додаток, використовується IPC (файл може використовуватися, а може і не використовуватися між ними). То в чому проблема?
Тодд,

Це питання стосується iPhone / iPad. Незважаючи на це, поки документ Apple не рекомендує інакше, NSUserDefaults призначений для невеликих налаштувань додатків, а не для зберігання. Як зазначено ( developer.apple.com/Library/mac/documentation/Cocoa/Reference/… ), "Клас NSUserDefaults забезпечує зручні методи доступу до загальнотипових типів, таких як плаваючі, подвійні, цілі числа, булеві значення та URL-адреси"
chris stamper

@cdstamper, який рекомендований спосіб зберігання, скажімо, зображення профілю для програми, де користувач увійшов до системи? Я не хочу використовувати CoreData лише для зберігання 1 зображення, і я відчуваю, що натискаю хмарне сховище / db кожного разу, коли мені потрібно показати, що зображення просто дороге / непотрібне.
AnBisw,

2
@annjawn просто напишіть його у файлову систему та збережіть URL у NSUserDefaults
chris stamper

29

Незважаючи на те, що можна зберегти 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

Цей метод дозволяє завантажувати будь-які UIImageз NSUserDefaults.

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

Завантаження з каталогу документів

Цей метод дозволяє завантажувати будь-яку 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);

Збереження декількох зображень UII у бібліотеці фотографій

Цей метод дозволяє зберегти кілька 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

У цьому випадку я використовував шлях до каталогу документів як аргумент. Однак ви можете використовувати будь-який каталог, до якого ви маєте доступ, у пісочниці / наборі програми.
Фернандо Сервантес

11

Для Свіфта 4

Я майже все спробував у цьому питанні, але для мене ніхто не працює. і я знайшов своє рішення. спочатку я створив розширення для UserDefaults, як показано нижче, а потім просто викликав методи get and 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)
    } 
}

щоб встановити зображення як фон у налаштуваннях ВК я використовував код нижче.

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

Але не доцільно, тому що списки не є відповідними місцями для великих крапок двійкових даних, особливо User Prefs. Було б краще зберегти зображення в папці документів користувача та зберегти посилання на цей об'єкт як URL-адресу або шлях.


4

Для Swift 3 та JPG формату

Зареєструвати зображення за замовчуванням:

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, 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 та 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

Свіфт 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
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.