将UIColor保存到NSUserDefaults并从中加载

57

在将UIColor保存到NSUserDefaults中并检索出来的过程中,最简单的方法是什么?


1
将UIColor保存为原始二进制数据而不使用NSKeyedArchiver//NSKeyedUnarchiver(NSCoding)https://dev59.com/XVsX5IYBdhLWcg3wALXa#34366333 - Leo Dabus
8个回答

134

可能有一种方法是将其归档(就像使用NSColor一样,尽管我还没有测试过):

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

并且要取回它:

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

3
UIColor实现了NSCoding协议,因此可以进行归档。 - benzado
3
这种方式还会保留与颜色相关的色彩配置文件。如果这对你不重要,那当然可以选择其他方式。 - Wevah
@Wevah 这是我尝试保存精灵颜色的方式,但程序崩溃了:NSData *colorData = [NSKeyedArchiver archivedDataWithRootObject:_mySprite.color]; [[NSUserDefaults standardUserDefaults] setObject:colorData forKey:@"spriteColor"]; - Jason
2
在Swift中:var colorData:NSData = NSKeyedArchiver.archivedDataWithRootObject(color); defaults.setObject(colorData, forKey: "BackgroundColor"); - FredL
看看我的答案,这是一个现代化的改进 :) - Andrew
显示剩余6条评论

77

使用被认可的答案,你的代码中将很快出现许多NSKeyed存档和解档。更清晰的解决方案是扩展UserDefaults。这正是扩展的用途; UserDefaults可能不知道UIColor,因为UIKit和Foundation是不同的框架。

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

    }

}

使用方法

UserDefaults.standard.set(UIColor.white, forKey: "white")
let whiteColor = UserDefaults.standard.color(forKey: "white")

使用 Objective-C 的类别也可以实现此操作。

我已将 Swift 文件添加到 此处 作为要点。


2
你也可以使用 Objective-C 中的分类来实现这个。 - Wevah

7
我已经自己找到答案了。
保存。
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"];

加载

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

5
唯一可能出现的问题是底层的CGColor不在RGB颜色空间中。如果你确定它将是RGB,那么这可能比archival更好的选择。 - Wevah
除了 RGB 假设之外,一旦您有两种或更多颜色需要保留,这个解决方案就会变得很烦人。理想情况下,您希望将所有颜色信息存储在单个键下,而不是四个,然后编写一个通用的 get/set 函数。 - benzado

5
感谢 Erica的UIColor类别。我不是很喜欢在首选项中保存4个浮点数,只想要一个单一的条目。 因此,使用Erica的UIColor类别,我可以将RGB颜色转换为/从可以保存在首选项中的NSString
// 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];
}

6
查看 [NSUserDefaults registerDefaults:] 来设置默认值(这样在读取值时就可以避免 if/else)。 - benzado
UIColor-Expanded.m和UIColor-Expanded.h文件可以在这里找到:https://github.com/ars/uicolor-utilities - Stéphane B.

2

Swift 3, UserDefaults扩展

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

使用示例

let colorKey = "favoriteColor"

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

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


这个答案基于之前的回答,并针对Swift 3进行了更新。


2

我需要在用户默认设置中存储UIColor对象的数组。正如其他答案所述,想法是将UIColor转换为数据并保存该数据。我对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
   }
}

使用方法:

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

    }
}

1

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

-4

编辑2:我似乎已经找到了答案。请查看Erica Sadun关于扩展UIColor的文章。

编辑:这段代码似乎不能用于UIColor对象。不确定为什么...

以下是一些要查看的代码:

将对象保存到NSUserDefaults中:

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

从NSUserDefaults读取对象:

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

感谢您的帮助,但似乎NSUserDefaults无法保存UIColor对象。 - Unreality
让我试一下我上面发布的代码 - 我只是凭记忆写的。 - zpesk
请查看我在帖子正文中链接的文章。 - zpesk
这个不起作用,因为NSUserDefaults只能存储属性列表对象:NSString、NSNumber、NSDate、NSData、NSArray和NSDictionary(其中所有键必须是字符串)。任何其他类型的数据都必须编码为其中之一。 - benzado

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接