Bilder in NSUserDefaults speichern?

80

Ist es möglich, Bilder NSUserDefaultsals Objekt zu speichern und zur weiteren Verwendung abzurufen?

Florik
quelle

Antworten:

84

BEACHTUNG! WENN SIE UNTER iOS8 / XCODE6 ARBEITEN, SEHEN SIE MEIN UPDATE UNTEN

Für diejenigen, die hier noch nach einer Antwort suchen, gibt es den Code "ratsam", um Bilder in NSUserDefaults zu speichern. Sie sollten Bilddaten NICHT direkt in NSUserDefaults speichern!

Daten schreiben:

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

Daten lesen:

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

Für iOS8 / XCODE6 Wie in den Kommentaren unten erwähnt, gibt es ein Problem mit xcode6 / ios8. Der Unterschied zwischen dem Installationsprozess von xcode5 und xcode 6 besteht darin, dass xcode6 die UUID von Apps nach jeder Ausführung in xcode ändert (siehe hervorgehobenes Teil im Pfad: / var / mobile / Container / Daten / Anwendung / B0D49CF5-8FBE-4F14-87AE-FA8C16A678B1 / Documents / image.jpg).

Es gibt also zwei Problemumgehungen:

  1. Überspringen Sie dieses Problem, da die einmal installierte App auf einem realen Gerät die UUID nie mehr ändert (tatsächlich, aber es ist eine neue App).
  2. Speichern Sie den relativen Pfad im gewünschten Ordner (in unserem Fall im Stammverzeichnis der App).

Hier ist eine schnelle Version des Codes als Bonus (mit 2. Ansatz):

Daten schreiben:

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

Daten lesen:

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
}
Nikita nahm
quelle
2
Code funktionierte beim ersten Mal nicht, konnte das Image nicht von der Festplatte abrufen. Der Pfad zum Dokumentordner war zwischen den App-Läufen unterschiedlich. könnte ein Problem von absoluten versus relativen Pfaden sein? Der Code schrieb in einen * imagePath, benötigte jedoch in der nächsten App-Ausführung einen anderen * imagePath, um die Datei abzurufen. Eine einfache Lösung bestand darin, nur den Dateinamen in nsuserdefaults zu speichern. Beim nächsten Ausführen der App konnten der Pfad des Dokumentenordners, der Dateiname und der Rest des Codes erneut abgerufen werden. (danke Nikita nahm)
tmr
Das iOS8 / Xcode 6-Problem ist also im Wesentlichen vollständig ungültig, wenn es hauptsächlich auf einem Gerät ausgeführt wird.
Micnguyen
Ich möchte nur hinzufügen: Durch die Verwendung von PNGRepresentation werden Orientierungsmetadaten nicht automatisch gespeichert. Sie müssen dies manuell tun oder einfach die JPEG-Version verwenden.
Micnguyen
Beeinflusst das Speichern von Bildern an diesem Speicherort die iCloud-Daten. Wenn sie nicht vom Benutzer generiert wurden, sollten wir sie nicht an diesem Speicherort speichern.
Chris Harrison
1
@NikitaTook Was ist mit einer Reihe von Bildern? Wie würde ich das machen?
GJZ
138

So speichern Sie ein Bild in NSUserDefaults:

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

So rufen Sie ein Bild von NSUserDefaults ab:

NSData* imageData = [[NSUserDefaults standardUserDefaults] objectForKey:key];
UIImage* image = [UIImage imageWithData:imageData];
Bonny
quelle
17
Technisch korrekt, definitiv nicht zu empfehlen. NSUserDefaults gibt auf einer Liste aus, die offensichtlich NICHT der Ort für Rohbilddaten ist.
CD-Stamper
@cdstamper Ich habe gelesen, dass eine Plist heutzutage im Binärformat auf Mac OSX gespeichert ist. Bei Verwendung zwischen einer Erweiterung und einer App wird IPC verwendet (eine Datei kann dazwischen verwendet werden oder nicht). Also, was ist das Problem?
Todd
Diese Frage richtet sich an iPhone / iPad. Unabhängig davon, bis Apples Dokument etwas anderes empfiehlt, ist NSUserDefaults für kleine Anwendungseinstellungen und nicht für Speicher gedacht. Wie angegeben ( developer.apple.com/Library/mac/documentation/Cocoa/Reference/… ), "bietet die NSUserDefaults-Klasse bequeme Methoden für den Zugriff auf gängige Typen wie Floats, Doubles, Integer, Booleans und URLs"
cdstamper
@cdstamper Was ist die empfohlene Methode zum Speichern von Profilbildern für eine App, in der der Benutzer angemeldet ist? Ich möchte CoreData nicht nur zum Speichern von 1 Bild verwenden und möchte jedes Mal auf den Cloud-Speicher / die Cloud zugreifen, wenn ich zeigen möchte, dass das Bild nur teuer / nicht erforderlich ist.
AnBisw
2
@annjawn schreiben Sie es einfach in das Dateisystem und speichern Sie die URL in NSUserDefaults
cdstamper
29

Das Speichern von UIImageto ist zwar möglich NSUserDefaults, wird jedoch häufig nicht empfohlen, da dies nicht die effizienteste Methode zum Speichern von Bildern ist. Eine effizientere Möglichkeit besteht darin, Ihr Bild in der Anwendung zu speichern Documents Directory.

Für die Zwecke dieser Frage habe ich die Antwort an Ihre Frage angehängt, zusammen mit der effizienteren Methode zum Speichern von a UIImage.


NSUserDefaults (nicht empfohlen)

Speichern in NSUserDefaults

Diese Methode ermöglicht es Ihnen , alle retten UIImagezu 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];
}

So nennt man es:

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

Laden aus NSUserDefaults

Mit dieser Methode können Sie beliebige UIImagevon laden NSUserDefaults.

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

So nennt man es:

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

Eine bessere Alternative

Speichern im Dokumentenverzeichnis

Mit dieser Methode können Sie alle UIImagein Documents Directoryder App speichern .

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

So nennt man es:

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

Laden aus dem Dokumentenverzeichnis

Mit dieser Methode können Sie alle UIImageaus der Anwendung laden Documents Directory.

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

    return result;
}

So nennt man es:

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

Eine andere Alternative

Speichern von UIImage in der Fotobibliothek

Diese Methode ermöglicht das Speichern UIImageauf dem Gerät Photo Libraryund wird wie folgt aufgerufen:

UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);

Speichern mehrerer UIImages in der Fotobibliothek

Mit dieser Methode können Sie mehrere UIImagesauf dem Gerät speichern 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);
    }
}

So nennt man es:

[self saveImagesToPhotoAlbums:images];

Wo imagesist Ihr NSArraybestehend aus UIImages.

Fernando Cervantes
quelle
Guter Eintrag! Wie finden Sie heraus, welches Verzeichnis für die Eingabe festgelegt werden soll inDirectory:(NSString *)directoryPath- können Sie es einfach so nennen, wie Sie möchten, wie "myAppData"?
Supertecnoboff
In diesem Fall habe ich den Pfad des Dokumentenverzeichnisses als Argument verwendet. Sie können jedoch jedes Verzeichnis verwenden, auf das Sie in der Sandbox / im Bundle Ihrer App zugreifen können.
Fernando Cervantes
10

Für Swift 4

Ich habe fast alles in dieser Frage versucht, aber niemand ist für mich gearbeitet. und ich fand meine Lösung. Zuerst habe ich eine Erweiterung für UserDefaults wie unten erstellt und dann einfach get and set Methoden aufgerufen.

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

Um das Bild als Hintergrund in settingsVC festzulegen, habe ich den folgenden Code verwendet.

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:

let bgImage = imageDefaults.imageForKey(key: "imageDefaults")!
Bilal Şimşek
quelle
imageDefaults?)
Oleksandr
let imageDefaults = UserDefaults.standard
Bilal Şimşek
8

Für schnelle 2.2

Lagern:

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

So rufen Sie ab:

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

Ja, technisch möglich wie in

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

Dies ist jedoch nicht ratsam, da Plists keine geeigneten Orte für große Blobs von Binärdaten sind, insbesondere für Benutzereinstellungen. Es ist besser, das Bild im Ordner "Benutzerdokumente" zu speichern und den Verweis auf dieses Objekt als URL oder Pfad zu speichern.

Warren Burton
quelle
4

Für Swift 3 und JPG- Format

Standardbild registrieren:

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

Bild speichern :

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

Bild laden :

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

Aus der Apple-Dokumentation,

Die NSUserDefaults-Klasse bietet bequeme Methoden für den Zugriff auf gängige Typen wie Floats, Doubles, Integer, Boolesche Werte und URLs. Ein Standardobjekt muss eine Eigenschaftsliste sein, dh eine Instanz von (oder für Sammlungen eine Kombination von Instanzen von): NSData, NSString, NSNumber, NSDate, NSArray oder NSDictionary. Wenn Sie einen anderen Objekttyp speichern möchten, sollten Sie ihn normalerweise archivieren, um eine Instanz von NSData zu erstellen.

Sie können das Bild folgendermaßen speichern: -

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

Und lesen Sie so: -

 NSData* imageData = [[NSUserDefaults standardUserDefaults]objectForKey:@"key_for_your_image"];
    UIImage* image = [UIImage imageWithData:imageData];
Suraj K Thomas
quelle
2

Es ist technisch möglich, aber nicht ratsam. Speichern Sie das Image stattdessen auf der Festplatte. NSUserDefaults ist für kleine Einstellungen gedacht, nicht für große Binärdatendateien.

Benjamin Mayo
quelle
1

Bild in NSUserDefault speichern:

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

Bild von NSUserDefault laden:

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]];
Chetan Bhalara
quelle
1

Ja, du kannst verwenden. Da es sich jedoch um die Speicherung von Einstellungen handelt, können Sie Bilder besser im Dokumentordner speichern.

Und Sie können den Pfad NSUserDefaultsbei Bedarf in der haben.

Ilanchezhian
quelle
1

Da diese Frage einen hohen Google-Suchindex hat, finden Sie hier die Antwort von @ NikitaTook in der heutigen Zeit, dh Swift 3 und 4 (mit Ausnahmebehandlung).

Hinweis: Diese Klasse dient ausschließlich zum Lesen und Schreiben von Bildern im JPG-Format in das Dateisystem. Das UserdefaultsZeug sollte außerhalb davon gehandhabt werden.

writeFileNimmt den Dateinamen Ihres JPG-Bildes (mit der Erweiterung .jpg) und das UIImageselbst auf und gibt true zurück, wenn es speichern kann, oder false, wenn es das Bild nicht schreiben kann. An diesem Punkt können Sie das Bild in Userdefaultswelchem speichern wäre Ihr Backup-Plan oder versuchen Sie es einfach noch einmal. Die readFileFunktion nimmt den Namen der Bilddatei auf und gibt a zurück UIImage. Wenn der an diese Funktion übergebene Bildname gefunden wird, gibt sie das Bild zurück. Andernfalls wird nur ein Standard-Platzhalterbild aus dem Asset-Ordner der App zurückgegeben (auf diese Weise können Sie böse Abstürze oder andere vermeiden seltsame Verhaltensweisen).

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
    }
}
AnBisw
quelle
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
}
Abdul Karim
quelle