Question

I'm using an NSGradient for some stuff and at some point I need to add/change/delete colors. Now NSGradient is immutable as far as I know so the only way I know how to do this is by creating a new gradient and changing the colors and their locations in the process. I have had no problem with this approach for adding colors and for changing the colors' position but for deleting colors something weird happens.

Here's the function that deletes a color from the gradient 'gradient' being given its index:

- (void)_deleteColorAtIndex: (NSInteger)colorIndex
{
    if (!([self.gradient numberOfColorStops] > 2)) { return; }

    NSMutableArray* newColors = [NSMutableArray arrayWithCapacity: [self.gradient numberOfColorStops] - 1];
    CGFloat locations[[self.gradient numberOfColorStops] - 1];

    NSLog(@"Gradient before:");
    int i;
    for (i = 0; i < [self.gradient numberOfColorStops]; i++) {
        NSColor* color;
        CGFloat location;
        [self.gradient getColor: &color location: &location atIndex: i];
        NSLog(@" - Color: %@ / Location: %.2f", color, location);
    }

    //int i;
    NSLog(@"Adding this to new gradient:");

    // ----------------------------
    for (i = 0; i < [self.gradient numberOfColorStops]; i++) {
        NSColor* color;
        CGFloat location;
        [self.gradient getColor: &color location: &location atIndex: i];

        if (colorIndex != i) {
            [newColors addObject: color];
            locations[i] = location;
            NSLog(@" - Color: %@ / Location: %.2f", color, location);
        }
    }

    self.gradient = [[NSGradient alloc] initWithColors: newColors atLocations: locations colorSpace: [self.gradient colorSpace]];

    // ----------------------------        

    NSLog(@"Gradient after:");
    //int i;
    for (i = 0; i < [self.gradient numberOfColorStops]; i++) {
        NSColor* color;
        CGFloat location;
        [self.gradient getColor: &color location: &location atIndex: i];
        NSLog(@" - Color: %@ / Location: %.2f", color, location);
    }
}

The important code is mainly between the two "// ----------------------------" thingies. The rest is just for debugging purposes.


The problem

When I run this code with a gradient like |-white----gray----black-| and try to delete the second color (gray, colorIndex = 1) I get this gradient: |-whiteblack---------|. The last color jumps to position 0.0. Here's the printed text of that code:

Gradient before:
- Color: NSCustomColorSpace Generic RGB colorspace 1 1 1 1 / Location: 0.00
- Color: NSCustomColorSpace Generic RGB colorspace 0.5 0.5 0.5 1 / Location: 0.50
- Color: NSCustomColorSpace Generic RGB colorspace 0 0 0 1 / Location: 1.00
Adding this to new gradient:
- Color: NSCustomColorSpace Generic RGB colorspace 1 1 1 1 / Location: 0.00
- Color: NSCustomColorSpace Generic RGB colorspace 0 0 0 1 / Location: 1.00 <- cool here
Gradient after:
- Color: NSCustomColorSpace Generic RGB colorspace 1 1 1 1 / Location: 0.00
- Color: NSCustomColorSpace Generic RGB colorspace 0 0 0 1 / Location: 0.00 <- disaster here

Why does this happen??? It makes no sense at all!! (well, come to think of it, almost no bug does until you have found the solution, but still....). Thanks!

Was it helpful?

Solution

The line

locations[i] = location;

is incorrect. Here, i is index into the original gradient rather than the index into the new color array. Among other solutions, you could use:

locations[[newColors count]-1] = location;

or just add a new index counter j that you increment only when adding to the newColors array, and use that index when assigning to locations:

[newColors addObject:color];
locations[j++] = location;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top