Saving UIColor to and loading from NSUserDefaults

Objective CCocoa TouchNsuserdefaultsUicolor

Objective C Problem Overview


What's the easiest way to save a UIColor into NSUserDefaults and then get it back out?

Objective C Solutions


Solution 1 - Objective C

One way of doing it might be to archive it (like with NSColor, though I haven't tested this):

NSData *colorData = [NSKeyedArchiver archivedDataWithRootObject:color];
[[NSUserDefaults standardUserDefaults] setObject:colorData forKey:@"myColor"];

And to get it back:

NSData *colorData = [[NSUserDefaults standardUserDefaults] objectForKey:@"myColor"];
UIColor *color = [NSKeyedUnarchiver unarchiveObjectWithData:colorData];

Solution 2 - Objective C

With the accepted answer, you'll quickly end up with a lot of NSKeyed archives & unarchives all over your code. A cleaner solution is to extend UserDefaults. This is exactly what extensions are for; UserDefaults probably doesn't know about UIColor as it is because UIKit and Foundation are different frameworks.

Swift
extension UserDefaults {

    func color(forKey key: String) -> UIColor? {
        var color: UIColor?
        if let colorData = data(forKey: key) {
            color = NSKeyedUnarchiver.unarchiveObject(with: colorData) as? UIColor
        }
        return color
    }

    func set(_ value: UIColor?, forKey key: String) {
        var colorData: Data?
        if let color = value {
            colorData = NSKeyedArchiver.archivedData(withRootObject: color)
        }
        set(colorData, forKey: key)
    }

}

Swift 4.2

extension UserDefaults {
    
    func color(forKey key: String) -> UIColor? {
        
        guard let colorData = data(forKey: key) else { return nil }
        
        do {
            return try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: colorData)
        } catch let error {
            print("color error \(error.localizedDescription)")
            return nil
        }
        
    }
    
    func set(_ value: UIColor?, forKey key: String) {
        
        guard let color = value else { return }
        do {
            let data = try NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: false)
            set(data, forKey: key)
        } catch let error {
            print("error color key data not saved \(error.localizedDescription)")
        }
        
    }
    
}
Usage
UserDefaults.standard.set(UIColor.white, forKey: "white")
let whiteColor = UserDefaults.standard.color(forKey: "white")

This can also be done in Objective-C with a category.

I've added the Swift file as a gist here.

Solution 3 - Objective C

I've got the answer by myself

Save

const CGFloat  *components = CGColorGetComponents(pColor.CGColor);
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setFloat:components[0]  forKey:@"cr"];
[prefs setFloat:components[1]  forKey:@"cg"];
[prefs setFloat:components[2]  forKey:@"cb"];
[prefs setFloat:components[3]  forKey:@"ca"];

Load

NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
UIColor* tColor = [UIColor colorWithRed:[prefs floatForKey:@"cr"] green:[prefs floatForKey:@"cg"] blue:[prefs floatForKey:@"cb"] alpha:[prefs floatForKey:@"ca"]];

Solution 4 - Objective C

Thanks for Erica's UIColor category. I did not really like saving 4 floats in the preferences, and just wanted a single entry.

So using Erica's UIColor category, I was able to convert the RGB color to/from an NSString which can be saved in the preferences.

// Save a color
NSString *theColorStr = [self.artistColor stringFromColor];
[[NSUserDefaults standardUserDefaults] setObject:theColorStr forKey:@"myColor"];

// Read a color
NSString *theColorStr = [[NSUserDefaults standardUserDefaults] objectForKey:@"myColor"];
if ([theColorStr length] > 0) {
	self.myColor = [UIColor colorWithString:theColorStr];
} else {
	self.myColor = [UIColor colorWithRed:88.0/255.0 green:151.0/255.0 blue:237.0/255.0 alpha:1.0];
}

Solution 5 - Objective C

I needed to store array of UIColor objects in User Defaults. Idea, as stated in other answers, is to convert UIColor to Data and save that data. I've made extension on UIColor:

extension UIColor {
   func data() -> Data {
      return NSKeyedArchiver.archivedData(withRootObject: self)
   }
   class func color(withData data: Data) -> UIColor? {
      return NSKeyedUnarchiver.unarchiveObject(with: data) as? UIColor
   }
}

Usage:

fileprivate var savedColors: [UIColor]? {
    get {
        if let colorDataArray = UserDefaults.standard.array(forKey: Constants.savedColorsKey) as? [Data] {
            return colorDataArray.map { UIColor.color(withData: $0)! }
        }
        return nil
    }
    set {
        if let colorDataArray = newValue?.map({ $0.data() }) {
            UserDefaults.standard.set(colorDataArray, forKey: Constants.savedColorsKey)
        }
        
    }
}

Solution 6 - Objective C

Swift 3, UserDefaults Extension

extension UserDefaults {

	internal func color(forKey key: String) -> UIColor? {
		guard let colorData = data(forKey: key) else {
			return nil
		}

		return NSKeyedUnarchiver.unarchiveObject(with: colorData) as? UIColor
	}

	internal func setColor(_ color: UIColor?, forKey key: String) {
		let colorData: Data?
		if let color = color {
			colorData = NSKeyedArchiver.archivedData(withRootObject: color)
		}
		else {
			colorData = nil
		}

		set(colorData, forKey: key)
	}
}

Example Use

let colorKey = "favoriteColor"

UserDefaults.standard.setColor(UIColor.red, forKey: colorKey)
let favoriteColor = UserDefaults.standard.color(forKey: colorKey)

print("favoriteColor is red: '\(favoriteColor == UIColor.red)'")


This answer is based on a previous answer. It is updated for Swift 3.

Solution 7 - Objective C

Swift

private let colorPickerKey = "ColorPickerKey"

var selectedColor: UIColor? {
    get {
        guard let colorData = UserDefaults.standard.object(forKey: colorPickerKey) as? Data,
            let color = NSKeyedUnarchiver.unarchiveObject(with: colorData) as? UIColor else { return nil }
        return color
    } set {
        guard let newValue = newValue else {
            UserDefaults.standard.removeObject(forKey: colorPickerKey)
            return
        }
        let colorData = NSKeyedArchiver.archivedData(withRootObject: newValue)
        UserDefaults.standard.set(colorData, forKey: colorPickerKey)
    }
}

Solution 8 - Objective C

Edit 2: I seem to have found the answer. Check out the article by Erica Sadun on extending UIColor.

Edit: This code does not seem to work for a UIColor Object. Not sure why...

Here is some code to take a look at:

Saving an object into NSUserDefaults:

 NSUserDefaults *userDefaults =[NSUserDefaults standardUserDefaults];
[userDefaults setObject:someColorObject forKey:@"myColor"];

Reading an object from NSUserDefaults:

NSUserDefaults *userDefaults =[NSUserDefaults standardUserDefaults];
UIColor *someColor = (UIColor *)[userDefaults objectForKey:@"myColor"];

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionUnrealityView Question on Stackoverflow
Solution 1 - Objective CWevahView Answer on Stackoverflow
Solution 2 - Objective CAndrewView Answer on Stackoverflow
Solution 3 - Objective CUnrealityView Answer on Stackoverflow
Solution 4 - Objective ClifjoyView Answer on Stackoverflow
Solution 5 - Objective CJovan StankovicView Answer on Stackoverflow
Solution 6 - Objective CMobile DanView Answer on Stackoverflow
Solution 7 - Objective CWarif Akhand RishiView Answer on Stackoverflow
Solution 8 - Objective CzpeskView Answer on Stackoverflow