6

I need to darken a UIImageView when it gets touched, almost exactly like icons on the springboard (home screen).

Should I be added UIView with a 0.5 alpha and black background. This seems clumsy. Should I be using Layers or something (CALayers).

Jonathan.
  • 53,997
  • 54
  • 186
  • 290

3 Answers3

6

I would let a UIImageView handle the actual drawing of the image, but toggle the image to one that's been darkened in advance. Here's some code I've used to generate darkened images with alpha maintained:

+ (UIImage *)darkenImage:(UIImage *)image toLevel:(CGFloat)level
{
    // Create a temporary view to act as a darkening layer
    CGRect frame = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
    UIView *tempView = [[UIView alloc] initWithFrame:frame];
    tempView.backgroundColor = [UIColor blackColor];
    tempView.alpha = level;

    // Draw the image into a new graphics context
    UIGraphicsBeginImageContext(frame.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [image drawInRect:frame];

    // Flip the context vertically so we can draw the dark layer via a mask that
    // aligns with the image's alpha pixels (Quartz uses flipped coordinates)
    CGContextTranslateCTM(context, 0, frame.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextClipToMask(context, frame, image.CGImage);
    [tempView.layer renderInContext:context];

    // Produce a new image from this context
    CGImageRef imageRef = CGBitmapContextCreateImage(context);
    UIImage *toReturn = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    UIGraphicsEndImageContext();
    [tempView release];
    return toReturn;
}
Kieran Harper
  • 1,098
  • 11
  • 22
4

How about subclassing UIView and adding a UIImage ivar (called image)? Then you could override -drawRect: something like this, provided you had a boolean ivar called pressed that was set while touched.

- (void)drawRect:(CGRect)rect
{
[image drawAtPoint:(CGPointMake(0.0, 0.0))];

// if pressed, fill rect with dark translucent color
if (pressed)
    {
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextSaveGState(ctx);
    CGContextSetRGBFillColor(ctx, 0.5, 0.5, 0.5, 0.5);
    CGContextFillRect(ctx, rect);
    CGContextRestoreGState(ctx);
    }
}

You would want to experiment with RGBA values above. And, of course, non-rectangular shapes would require a bit more work - like a CGMutablePathRef.

westsider
  • 4,967
  • 5
  • 36
  • 51
  • 1
    The UIImage on question happen to be in a subclasses UIView already so I can do this. However why would I do this in the drawRect method, couldn't I do it directly in the touches began? – Jonathan. Oct 24 '10 at 00:07
  • If it's a matter of toggling some setting, then that would happen in touchesBegan. And it might be toggled back in touchesEnded. But the actual drawing would, I think, happen in drawRect. You might need to call setNeedsDisplay on your UIView subclass instance in conjunction with toggled state changes. (This might be best done in custom setter.) I am not sure how a subclass of UIImageView would behave if its drawRect were overridden. This is why I suggested the 'safer' approach of basically writing your own UIImageView. Hope this helps. – westsider Oct 24 '10 at 00:49
  • you misunderstood my comment, why must the custom drawing happen in drawRect. Why I can't I put all the CGContext... Code in touchesBegan. – Jonathan. Oct 24 '10 at 08:11
  • @Jonathan, to quote Joe Conway and Aaron Hillegass from page 91 of their book "iPhone Programming: The Big Nerd Ranch Guide", 'The drawRect: method is where the drawing code for the view goes.' I always do my Core Graphics drawing within drawRect:. You might want to read through this for more details. I have been unable to find docs that warn against drawing outside of drawRect:; thought I read something like that... – westsider Oct 24 '10 at 20:55
1

UIImageView can have multiple images; you could have two versions of the image and switch to the darker one when needed.

jamihash
  • 1,900
  • 1
  • 12
  • 15