UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Reading rgba from a UIColor, but getting junk values in some cases

Forums > Swift

I'm trying to pull rgb values from a UIColor (like here: https://www.hackingwithswift.com/example-code/uicolor/how-to-read-the-red-green-blue-and-alpha-color-components-from-a-uicolor) and it's mostly working. But for some colors, I'm getting unexpected results after they're selcted from a ColorPicker.

Within an ios app, if I pass UIColor(red: 0.11873, green: 0.296969, blue: 0.386893, alpha: 1) to getRed, I get back the expected rgb values. Within my UI, I use a ColorPicker to select the same color, I then get back a negative value for red. Other colors selected via ColorPicker return expected results.

I'm stuck at this point. Are there ways to narrow this problem down more? Am I missing some understanding around how getRed should be working? Are there ways to filter out colors that just won't work this way to prevent the user selecting wrong colors from ColorPicker?

import UIKit
import SwiftUI
​
extension UIColor {
  var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
    var red: CGFloat = 0
    var green: CGFloat = 0
    var blue: CGFloat = 0
    var alpha: CGFloat = 0
    getRed(&red, green: &green, blue: &blue, alpha: &alpha)

    return (red, green, blue, alpha)
  }
}
​
struct ColorStuff {
  static func getRedPart(for uiColor: UIColor) -> Double {
    let (r, g, b, _) = uiColor.rgba
    return r
  }
}
​
struct ContentView: View {
  @State private var color = Color(UIColor(red: 0.11873, green: 0.296969, blue: 0.386893, alpha: 1))
​
  var body: some View {
    VStack {
      ColorPicker("", selection: $color, supportsOpacity: false)
      Rectangle()
        .frame(width: 200, height: 200)
        .foregroundColor(color)
      Text("red part: \(ColorStuff.getRedPart(for: UIColor(color)))")
    }
  }
}

2      

I can't really look into this much right now since I'm away from my laptop for the next several hours, but I' curious if getRed(_:green:blue:alpha:) returns true or false on those times when you're getting back a negative value.

2      

I will check on that now and report back.

2      

getRed returns true in both cases

2      

I can't explain why this happens, but I can suggest you try this instead and see if this will work for your needs:

extension Color {
    private func getComponent(_ index: Int) -> CGFloat {
        let cgColor = UIColor(self).cgColor
        guard cgColor.numberOfComponents >= 2,
              let value = cgColor.components?[index] else {
                  return 0.0
              }
        return value
    }

    var redValue: CGFloat {
        getComponent(0)
    }

    var greenValue: CGFloat {
        getComponent(1)
    }

    var blueValue: CGFloat {
        getComponent(2)
    }
}

struct ColorPartsView: View {
    @State private var color = Color(UIColor(red: 0.11873, green: 0.296969, blue: 0.386893, alpha: 1))

    var body: some View {
        VStack {
            ColorPicker("", selection: $color, supportsOpacity: false)
            Rectangle()
                .frame(width: 200, height: 200)
                .foregroundColor(color)
            Text("red part: \(color.redValue)")
            Text("green part: \(color.greenValue)")
            Text("value part: \(color.blueValue)")
        }
    }
}

Thi smight be a question for StackOverflow or even the Apple Developer forums.

2      

Thank you! That works for my test cases, so I'll apply it to the rest of my code. It will also simplify some things downstream, as I don't think I'll have to use UIColor at all.

2      

I must confess to not having a lot of understanding of this kind of thing, but it seems like the problem is that in iOS 10+ the system uses the extended sRGB color space, in which RGB values can fall outside the 0.0 to 1.0 range.

You can see this by altering the getComponent(_:) function like so:

extension Color {
    private func getComponent(_ index: Int) -> CGFloat {
        let uiColor = UIColor(self)
        print(uiColor)
        let cgColor = uiColor.cgColor
        print(cgColor)
        guard cgColor.numberOfComponents >= 2,
              let value = cgColor.components?[index] else {
                  return 0.0
              }
        return value
    }
}

and examing the output:

UIExtendedSRGBColorSpace 0.11873 0.296969 0.386893 1
<CGColor 0x600003522e80> [<CGColorSpace 0x60000353ca20> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1; extended range)] ( 0.11873 0.296969 0.386893 1 )
UIExtendedSRGBColorSpace 0.11873 0.296969 0.386893 1
<CGColor 0x600003522e20> [<CGColorSpace 0x60000353ca20> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1; extended range)] ( 0.11873 0.296969 0.386893 1 )
UIExtendedSRGBColorSpace 0.11873 0.296969 0.386893 1
<CGColor 0x600003522dc0> [<CGColorSpace 0x60000353ca20> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1; extended range)] ( 0.11873 0.296969 0.386893 1 )
2023-03-13 18:26:12.450865-0700 iOS15 SwiftUI Tests[14978:339523] [AXRuntimeCommon] Unknown client: iOS15 SwiftUI Tests
kCGColorSpaceModelRGB 0.076741 0.211901 0.283034 1 
<CGColor 0x600003501500> [<CGColorSpace 0x600003519c80> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; Display P3)] ( 0.076741 0.211901 0.283034 1 )
kCGColorSpaceModelRGB 0.076741 0.211901 0.283034 1 
<CGColor 0x600003501500> [<CGColorSpace 0x600003519c80> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; Display P3)] ( 0.076741 0.211901 0.283034 1 )
kCGColorSpaceModelRGB 0.076741 0.211901 0.283034 1 
<CGColor 0x600003501500> [<CGColorSpace 0x600003519c80> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; Display P3)] ( 0.076741 0.211901 0.283034 1 )

You can see that the UIColor uses the extended sRGB (sRGB IEC61966-2.1; extended range) color space and the CGColor uses Display P3 (Display P3).

Again, though, most of this color space stuff is beyond me so I'm not sure how to use this information to help you.

2      

I have also noticed that there are different color spaces, but I'm not sure what to do with that info, either.

While updating to your suggested way of pulling the rgb pieces, I have found that using Color(hue: 200, saturation: 70, brightness: 39) for the initial setting for color returns values way outside the range of 0 - 1.

I need to fiddle with it some more. Maybe someone who has a deeper understanding of the color spaces will chime in.

2      

Your solution is giving me more consistent behavior. For some of my tests, I create the Color using the sRGB color space. But I'm getting consistently usable values for any numbers I pick using the ColorPicker - and that was the important part. I think this is going to work for me. Thank you for your help!

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.