Skip to content

Instantly share code, notes, and snippets.

@vibrazy
Last active January 28, 2023 18:19
Show Gist options
  • Save vibrazy/b105e3138105f604ab1ee4cfcdb67075 to your computer and use it in GitHub Desktop.
Save vibrazy/b105e3138105f604ab1ee4cfcdb67075 to your computer and use it in GitHub Desktop.
SwiftUI `ColorPicker` load and save values to disk using `AppStorage`
//
// ContentView.swift
// Shared
//
// Created by Dan Tavares on 01/07/2022.
// More Info: https://nilcoalescing.com/blog/EncodeAndDecodeSwiftUIColor/
import SwiftUI
#if os(iOS)
typealias PlatformColor = UIColor
extension Color {
init(platformColor: PlatformColor) {
self.init(uiColor: platformColor)
}
}
#elseif os(macOS)
typealias PlatformColor = NSColor
extension Color {
init(platformColor: PlatformColor) {
self.init(nsColor: platformColor)
}
}
#endif
struct ContentView: View {
@AppStorage("color") var color: Color = Color.white
var body: some View {
ZStack {
color.edgesIgnoringSafeArea(.all)
ColorPicker("Background color", selection: $color)
.padding()
}
}
}
extension Color: RawRepresentable {
// TODO: Sort out alpha
public init?(rawValue: Int) {
let red = Double((rawValue & 0xFF0000) >> 16) / 0xFF
let green = Double((rawValue & 0x00FF00) >> 8) / 0xFF
let blue = Double(rawValue & 0x0000FF) / 0xFF
self = Color(red: red, green: green, blue: blue)
}
public var rawValue: Int {
guard let coreImageColor = coreImageColor else {
return 0
}
let red = Int(coreImageColor.red * 255 + 0.5)
let green = Int(coreImageColor.green * 255 + 0.5)
let blue = Int(coreImageColor.blue * 255 + 0.5)
return (red << 16) | (green << 8) | blue
}
private var coreImageColor: CIColor? {
return CIColor(color: PlatformColor(self))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
@donly
Copy link

donly commented Jul 1, 2022

Thanks! This works very well except for Mac... I got zero for rawValue. Is it possible to support Mac too?

extension Color: RawRepresentable {
    
    public init?(rawValue: String) {
        
        guard let data = Data(base64Encoded: rawValue) else {
            self = .black
            return
        }
        
        do {
            let color = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? UIColor ?? .black
            self = Color(color)
        } catch {
            self = .black
        }
        
    }
    
    public var rawValue: String {
        
        do {
            let data = try NSKeyedArchiver.archivedData(withRootObject: UIColor(self), requiringSecureCoding: false) as Data
            return data.base64EncodedString()
            
        } catch {
            return ""
        }
    }
}

use this solution pls

@vibrazy
Copy link
Author

vibrazy commented Jul 1, 2022

@limtc Can you try again?

@liquidsmok3
Copy link

Just wanted to say thank you, saved me a few hours.

@liquidsmok3
Copy link

after using this for a day , this does some weird things to the color picker that ultimately leads to a crash due to a memory error.

so what im seeing is, when I click on the color picker and try to select a color swatch on the grid tab, the color gets selected but the white selection box for the color doesn't stick. If I tap and hold the color the selection box stays visible.

On the sliders tab, if you move one slider for some reason the other 2 sliders start moving too like it's trying to compensate for your changes. For example, if all sliders are set to 255, moving the G slider (in RGB) to zero, the R slider now has a value of 25 and the B slider is now 93 without ever touching those sliders.
playing with the sliders at this point leads to an app crash!
watching the debugger while I play with color picker sliders, I see my app memory go from 25MB to 2.93GB then it crashes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment