views:

1556

answers:

2

I'm creating a simple color picker controlled by a custom slider type control.

I'm passing a float value from my slider to a UIColor constructor method and updating the background color of a CALayer as the value changes.

It runs really smooth in the simulator, but I get a lot of flickering when running on the device.

Is this just because it's over taxing the GPU? It's strangely the worst when moving through blue. And changing the alpha value and brightness flicker more than changing the saturation and hue.

Should I not pass arbitrary float values as the slider value changes? If not, how could I constrain this to less colors for optimization?

I thought of disabling implicit animations for the backgroundColor property of the CALayer, but I'm not sure how to do this.

Are there any tricks for optimizing UIColor changes.

Update

I'm not using drawRect: or drawInRect:

I'm just changing the backgroundColor property of a CALayer. Every time touchesMoved: is called I grab the location, create a 0.0 to 1.0 float value and pass a message to my delegate that the value has changed and call the -(void)updateCurrentColor method.

Here's an example of my implementation:

@implementation MyCustomSliderView


- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

    hueLocation = [[touches anyObject] locationInView:self];

    //Smoothing
    if ((hueLocation.x > previousHueLocation.x) && ((hueLocation.x - previousHueLocation.x) > 1.0) && (hueLocation.x - previousHueLocation.x) < 10.0) {
     hueLocation.x = previousHueLocation.x + 1.0;
    } 

    if ((hueLocation.x < previousHueLocation.x) && ((previousHueLocation.x - hueLocation.x) > 1.0) && (previousHueLocation.x - hueLocation.x) < 10.0) {
     hueLocation.x = previousHueLocation.x - 1.0;
    } 

    //Percentage of screen.
    hueValue = hueLocation.x/self.bounds.size.width;

    //Clip to 1.0 & 0.0
    if (hueValue > 1.0) {
     hueValue = 1.0;
    }
    if (hueValue < 0.0) {
     hueValue = 0.0;
    }

    NSLog(@"Value is: %f", hueValue);
    NSLog(@"Location %f", hueLocation.x); 

    previousHueLocation = hueLocation;

    if([delegate respondsToSelector:@selector(updateCurrentColor)]) {
        [delegate updateCurrentColor];
    }

}


@implementation MyViewController

- (void)updateCurrentColor {

    currentColor = [UIColor colorWithHue:hueSliderView.hueValue
            saturation:saturationSliderView.saturationValue
            brightness:brightnessSliderView.brightnessValue
              alpha:alphaSliderView.alphaValue];

    colorLayer.backgroundColor = currentColor.CGColor; 

}

At first I thought that it was jumping values due to the imprecision of the touch interface. The smoothing function helped, but it still flickers some, especially with the alpha value, even when my location only changes by 1 pixel. Should I constrain this to a limited amount of colors/values instead of just passing in an arbitrary float? Again, runs great in the simulator, seems to be just a performance issue.

+1  A: 

Drawing on the iPhone is double buffered. If you are getting flickering, it is because you are drawing one thing and then another -- it is not because of speed problems.

I would guess you are making one of the following mistakes:

  • Updating your color value and drawing your color square out of sync (so the color is not set correctly when you drawInRect:)
  • Failing to initialize/calculate your color correctly (so you are displaying invalid colors)
  • Erasing or redrawing your rectangle when you shouldn't

Another thing to check... colors in Cocoa are floats from zero to 1. Setting integer values from 0 to 255 (as used on the web or Windows) will result in seriously weird problems. If your color calculations are over 0 to 255, then divide by 255.0 before setting the UIColor.

Matt Gallagher
What does "double buffered" mean in this context?
erikprice
@Matt Gallagher, thanks, just updated question with my implementation.
Joe Ricioppo
@erikprice "double buffered" means that all drawing happens offscreen and then is copied onscreen once drawing is complete. This means that an erase to white, followed by paint black causes no onscreen flicker since the white state is never shown.
Matt Gallagher
+2  A: 

Disabling implicit animations eliminated the flickering.

I've updated the color changing methods so that the background color can be animated when called from touchesBegan:, but not implicitly animated when called from touchesMoved:

- (void)updateCurrentColorAnimated:(BOOL)animated {

    currentColor = [UIColor colorWithHue:hueSliderView.hueValue
            saturation:colorView.saturationValue
            brightness:colorView.brightnessValue
              alpha:alphaSlider.alphaValue];
    if (animated) {

     colorLayer.backgroundColor = currentColor.CGColor;

    } 

    if (!animated) {

     [CATransaction begin];

     [CATransaction setValue:(id)kCFBooleanTrue
          forKey:kCATransactionDisableActions];


     colorLayer.backgroundColor = currentColor.CGColor;

     [CATransaction commit];

    }

}

The reason I rolled my own slider is so that I can jump to new values instead of having to catch the little slider control perfectly, the interaction of which I found not very good.

Joe Ricioppo