views:

341

answers:

2

I'm trying to overlay a custom semi-transparent image over a base image. The overlay image is stretchable and created like this:

[[UIImage imageNamed:@"overlay.png"] stretchableImageWithLeftCapWidth:5.0 topCapHeight:5.0]

Then I pass that off to a method that overlays it onto the background image for a button:

- (void)overlayImage:(UIImage *)overlay forState:(UIControlState)state {
    UIImage *baseImage = [self backgroundImageForState:state];      

    CGRect frame = CGRectZero;
    frame.size = baseImage.size;

    // create a new image context
    UIGraphicsBeginImageContext(baseImage.size);        

    // get context
    CGContextRef context = UIGraphicsGetCurrentContext();   

    // clear context
    CGContextClearRect(context, frame);

    // draw images
    [baseImage drawInRect:frame];   
    [overlay drawInRect:frame];// blendMode:kCGBlendModeNormal alpha:1.0];

    // get UIImage
    UIImage *overlaidImage = UIGraphicsGetImageFromCurrentImageContext();

    // clean up context
    UIGraphicsEndImageContext();

    [self setBackgroundImage:overlaidImage forState:state];
}

The resulting overlaidImage looks mostly correct, it is the correct size, the alpha is blended correctly, etc. however it has vertical artifacts/noise.

UIImage artifacts example

(example at http://acaciatreesoftware.com/img/UIImage-artifacts.png)

I tried clearing the context first and then turning off PNG compression--which reduces the artifacting some (completely on non stretched images I think).

Does anyone know a method for drawing stretchable UIImages with out this sort of artifacting happening?

A: 

So the answer is: Don't do this. Instead you can paint your overlay procedurally. Like so:

- (void)overlayWithColor:(UIColor *)overlayColor forState:(UIControlState)state {
    UIImage *baseImage = [self backgroundImageForState:state];      

    CGRect frame = CGRectZero;
    frame.size = baseImage.size;

    // create a new image context
    UIGraphicsBeginImageContext(baseImage.size);        

    // get context
    CGContextRef context = UIGraphicsGetCurrentContext();   

    // draw background image
    [baseImage drawInRect:frame];   

    // overlay color
    CGContextSetFillColorWithColor(context, [overlayColor CGColor]);
    CGContextSetBlendMode(context, kCGBlendModeSourceAtop);
    CGContextFillRect(context, frame);

    // get UIImage
    UIImage *overlaidImage = UIGraphicsGetImageFromCurrentImageContext();

    // clean up context
    UIGraphicsEndImageContext();

    [self setBackgroundImage:overlaidImage forState:state];
}
Ben Lachman
A: 

Are you being too miserly with your original image, and forcing it to stretch rather than shrink? I've found best results out of images that fit the same aspect ratio and were reduced in size. Might not solve your problem tho.

pms1969
Interesting. I'll give it a try.
Ben Lachman