Nate Rose
Nate Rose

Reputation: 81

How do you save Color Picker values into UserDefaults in SwiftUI?

I have an array of colors assigned that I want saved to UserDefaults. I have a default of gray, green, orange, and red, but I want to allow a color picker to change the green orange and red. The default value works and shows in my Simulator, but when I try to change a color in the array I get an error "Attempt to insert non-property list object (\n "50% gray",\n green,\n "kCGColorSpaceModelRGB 0.647194 0.881984 0.980039 1 ",\n red\n) for key SavedColors." I believe this is because the color picker is trying to insert a color of a different type? It looks like it is trying to insert a CGColor or CGColorSpace maybe?

Here is my code for the project:

import SwiftUI
import Foundation
import Combine

class UserSettings: ObservableObject {
    @Published var colors: [Color] {
        didSet {
            UserDefaults.standard.set(colors, forKey: "SavedColors")
        }
    }
    init() {
        self.colors = UserDefaults.standard.object(forKey: "SavedColors") as? [Color] ?? [Color.gray.opacity(0.5), Color.green, Color.orange, Color.red]
    }
}

struct CustomizeView: View {
    @ObservedObject var savedColors = UserSettings()
    var body: some View {
        NavigationView {
            Form {
                if #available(iOS 14.0, *) {
                    ColorPicker("Select low priority color", selection: $savedColors.colors[1])
                } else {
                    Text("Select low priority color")
                }
                if #available(iOS 14.0, *) {
                    ColorPicker("Select normal priority color", selection: $savedColors.colors[2])
                } else {
                    Text("Select normal priority color")
                }
                if #available(iOS 14.0, *) {
                    ColorPicker("Select high priority color", selection: $savedColors.colors[3])
                } else {
                    Text("Select high priority color")
                }
            }.navigationBarTitle("Customize", displayMode: .inline)
      }
}

Upvotes: 8

Views: 3273

Answers (2)

protasm
protasm

Reputation: 1297

I'm doing this and it works:

@Published var usSearchTermColor: Color = Color(
    .sRGB,
    red: (UserDefaults.standard.object(forKey: "usSearchTermColor") as! [CGFloat])[0],
    green: (UserDefaults.standard.object(forKey: "usSearchTermColor") as! [CGFloat])[1],
    blue: (UserDefaults.standard.object(forKey: "usSearchTermColor") as! [CGFloat])[2],
    opacity: (UserDefaults.standard.object(forKey: "usSearchTermColor") as! [CGFloat])[3]
) {
    didSet {
        let components = self.usSearchTermColor.cgColor?.components ?? UIColor.yellow.cgColor.components

        UserDefaults.standard.set(components, forKey: "usSearchTermColor")
    }
}

Basically, storing the color information in UserDefaults as an array of CGFloat values, and assembling/disassembling whenever getting/setting.

I would be very happy to see a better way to do this!

Edit: credit to Indently @ this link for the concept.

Upvotes: 0

Ádám Nagy
Ádám Nagy

Reputation: 211

Hi!

I could achieve saving SwiftUI Color with this conversion on MacOS:

extension Color {

    /// Explicitly extracted Core Graphics color
    /// for the purpose of reconstruction and persistance.
    var cgColor_: CGColor {
        NSColor(self).cgColor
    }
}

extension UserDefaults {
    func setColor(_ color: Color, forKey key: String) {
        let cgColor = color.cgColor_
        let array = cgColor.components ?? []
        set(array, forKey: key)
    }

    func color(forKey key: String) -> Color {
        guard let array = object(forKey: key) as? [CGFloat] else { return .accentColor }
        let color = CGColor(colorSpace: CGColorSpace(name: CGColorSpace.sRGB)!, components: array)!
        return Color(color)
    }
}

Upvotes: 5

Related Questions