Cgcolorgetcomponents() Not Returning the Correct Values for Black and for White

CGColorGetComponents() not returning the correct values for black and for white

What you're not grasping is that nothing about CGColorGetComponents makes any claims or guarantees about how many components there are or what they mean. That information is contained in the color's color space. If you look at the color space of white and black, you will see they are both forms of grey:

let sp = CGColorGetColorSpace(UIColor.whiteColor().CGColor)
println(sp) // kCGColorSpaceDeviceGrey

Thus, they are expressed in just two values: the amount of grey, and the alpha.

Hence, white is [1.0,1.0] - which, if you ignore the extra two numbers which you should not have been reading in the first place, is exactly what you got.

Similarly, black is [0.0,1.0], which is also what you got.

What does CGColorGetComponents() return?

Yes, it returns an array of CGFloats. Specifically, it returns "an array of intensity values for the color components (including alpha) associated with the specified color."

The color components returned depend on what color space the passed CGColorRef uses.

More information can be found in the CGColor documentation.

Different behaviours for different colors in drawRect:

Similar to this answer I am guessing that darkGrayColor and lightGrayColor both represent gray-sacle values rather tan rgba-values. And therefore dont have 4 comoponents but only 2.

You might want / have to use a different approach of setting the color:

CGContextSetStrokeColorWithColor(context, [UIColor darkGrayColor].CGColor);

Is there an issue with CGColorGetComponents?

[UIColor whiteColor] and [UIColor blackColor] use [UIColor colorWithWhite:alpha:] to create the UIColor. Which means this CGColorRef has only 2 color components, not 4 like colors created with [UIColor colorWithRed:green:blue:alpha:].

Of course you can NSLog those too.

if (CGColorGetNumberOfComponents(myColorRef) == 2) {
const CGFloat *colorComponents = CGColorGetComponents(myColorRef);
NSLog(@"r=%f, g=%f, b=%f, a=%f", colorComponents[0], colorComponents[0], colorComponents[0], colorComponents[1]);
}
else if (CGColorGetNumberOfComponents(myColorRef) == 4) {
const CGFloat * colorComponents = CGColorGetComponents(myColorRef);
NSLog(@"r=%f, g=%f, b=%f, a=%f", colorComponents[0], colorComponents[1], colorComponents[2], colorComponents[3]);
}
else {
NSLog(@"What is this?");
}

Be aware that there are different colorSpaces too. So if you need this code for more than logging (e.g. saving RGBA strings to json) you have to check (and probably convert) the colorSpace too.

How to get color components of a CGColor correctly

Most likely wrong colorspace. I guess the alpha component of the grayscale colorspace gets where you think the green component should be.

I use this function to create a string from UIColor, I only encounter RGB and Grayscale colorspaces, so I just interpret every color with less than 4 (R+G+B+A) components as grayscale.

if (CGColorGetNumberOfComponents(color.CGColor) < 4) {
const CGFloat *components = CGColorGetComponents(color.CGColor);
color = [UIColor colorWithRed:components[0] green:components[0] blue:components[0] alpha:components[1]];
}
if (CGColorSpaceGetModel(CGColorGetColorSpace(color.CGColor)) != kCGColorSpaceModelRGB) {
NSLog(@"no rgb colorspace");
// do seomthing
}
const CGFloat *components = CGColorGetComponents(color.CGColor);
NSString *colorAsString = [NSString stringWithFormat:@"%f,%f,%f,%f", components[0], components[1], components[2], components[3]];

of course this method is not save for all cases, you should adopt it to your requirements.

Strange Behavior Of UIColor As Function Parameter In Swift

You could try something like this:

var colors: [UIColor] = []

func setColors(colors: UIColor...) {
self.colors = colors
}

override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
let colorSpace: CGColorSpaceRef = CGColorSpaceCreateDeviceRGB()

var gradientColors: [CGColor] = []
var colorLocations: [CGFloat] = []
var i: CGFloat = 0.0;

for color in colors {
gradientColors.append(color.CGColor)
colorLocations.append(i)
i += CGFloat(1.0) / CGFloat(colors.count)
}

let gradient: CGGradientRef = CGGradientCreateWithColors(colorSpace, gradientColors, colorLocations)

CGContextDrawLinearGradient(context, gradient, CGPoint(x: 0.0, y: self.frame.size.height / 2), CGPoint(x: self.frame.size.width, y: self.frame.size.height / 2), 0)
}

Side Note: You don't want to capitalize variable names. So I changed self.Colors to self.colors

What is the correct way to update RGB values of a colorLiteral in Xcodet?

Double click on the little square, this will pop up:

Sample Image

press "Other". The usual macOS color picker shows up. You can then go to the second tab, and select the RGB sliders option. There is also an option to enter floating point values if you click on the gear icon:

Sample Image

Is there a conventional method for inverting NSColor values?

Simple: The components are all 0–1, so subtract each component from 1 to get the complement's value.

Examples:

  • Red = 1, 0, 0; complement = 0, 1, 1 = cyan
  • Yellow = 1, 1, 0; complement = 0, 0, 1 = blue
  • White = 1, 1, 1; complement = 0, 0, 0 = black
  • Deep orange = 1, 0.25, 0; complement = 0, 0.75, 1 = sky blue

As you can guess, this is a symmetric operation. Inverting the inverse will get you exactly the color you started with.

Is it possible to map @Published to @Binding?

It is possible with computable Binding, like

PaintColor(color: color, isSelected:  Binding(
get: { state.selectedColor == color },
set: { state.selectedColor = $0 ? color : .white }
))
.frame(maxWidth: .infinity)

*actually if it is possible all unselected then it is needed to make selectedColor optional (and then reset to nil instead of .white above)

@Published var selectedColor: Color? = .white


Related Topics



Leave a reply



Submit