3

I may point out that Drawing and Rendering in Objective-C is my weakness. Now, here's my problem.

I want to add a 'Day/Night' feature to my game. It has got lots of objects on a map. Every object is a UIView containing some data in variables and some UIImageViews: the sprite, and some of the objects have a hidden ring (used to show selection).

I want to be able to darken the content of the UIView, but I can't figure out how. The sprite is a PNG with transparency. I've just managed to add a black rectangle behind the sprite using this:

CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);

CGContextSetRGBFillColor(ctx, 0, 0, 0, 0.5);
CGContextFillRect(ctx, rect);
CGContextRestoreGState(ctx);

As I've read, this should be done in the drawRect method. Help please!

If you want to understand better my scenario, the App where I'm trying to do this is called 'Kipos', at the App Store.

David Da Silva Contín
  • 1,051
  • 2
  • 10
  • 20

4 Answers4

2

Floris497's approach is a good strategy for a blanket darkening for more than one image at a time (probably more what you're after in this case). But here's a general purpose method to generate darker UIImages (while respecting alpha pixels):

+ (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
1

Here is how to do it:

// inputEV controlls the exposure, the lower the darker (e.g "-1" -> dark) 
-(UIImage*)adjustImage:(UIImage*)image exposure:(float)inputEV
{
    CIImage *inputImage = [[CIImage alloc] initWithCGImage:[image CGImage]];
    UIImageOrientation originalOrientation = image.imageOrientation;

    CIFilter* adjustmentFilter = [CIFilter filterWithName:@"CIExposureAdjust"];
    [adjustmentFilter setDefaults];
    [adjustmentFilter setValue:inputImage forKey:@"inputImage"];
    [adjustmentFilter setValue:[NSNumber numberWithFloat:-1.0] forKey:@"inputEV"];

    CIImage *outputImage = [adjustmentFilter valueForKey:@"outputImage"];
    CIContext* context = [CIContext contextWithOptions:nil];
    CGImageRef imgRef = [context createCGImage:outputImage fromRect:outputImage.extent] ;

    UIImage* img = [[UIImage alloc] initWithCGImage:imgRef scale:1.0 orientation:originalOrientation];

    CGImageRelease(imgRef);

    return img;    
}

Remember to import:

#import <QuartzCore/Quartzcore.h>

And add CoreGraphics and CoreImage frameworks to your project. Tested on iPhone 3GS with iOS 5.1 CIFilter is available starting from iOS 5.0.

Rafał Sroka
  • 39,540
  • 23
  • 113
  • 143
  • Thanks for the fully detailed answer! I seems to be exactly what I was looking for! :) I'll try it out, to check if it works/I am able to implement it, and I'll mark it as the correct answer, if so. – David Da Silva Contín Aug 04 '12 at 20:53
1

The best way would be to add a core image filter to the layer that darkened it. You could use CIExposureAdjust.

CIFilter *filter = [CIFilter filterWithName:@"CIExposureAdjust"];
[filter setDefaults];
[filter setValue:[NSNumber numberWithFloat:-2.0] forKey:@"inputEV"];
view.layer.filters = [NSArray arrayWithObject:filter];
joerick
  • 16,078
  • 4
  • 53
  • 57
0

draw a UIView (a black one) over it and set "User interaction enabled" to NO

hope you can do something with this.

then use this to make it dark

[UIView animateWithDuration:2
                      animations:^{nightView.alpha = 0.4;}
                          completion:^(BOOL finished){ NSLog(@"done making it dark"); ]; }];

to make it light

[UIView animateWithDuration:2
                      animations:^{nightView.alpha = 0.0;}
                          completion:^(BOOL finished){ NSLog(@"done making it light again"); ]; }];
Floris497
  • 1,406
  • 12
  • 18
  • But the image is not rectangular, it's irregular, like a character. That would cover it with a whole black rectangle, wouldn't it? – David Da Silva Contín Mar 03 '12 at 22:16
  • put it on top of your game view and it will be half transparent at night an fully transparent at day so you do not see it. if that is what you mean? – Floris497 Mar 03 '12 at 22:19
  • Hmm. Nice. I'll try that. Having User interaction enabled set to NO would let touches go through? – David Da Silva Contín Mar 03 '12 at 22:24
  • Yes so everything behind it will work as normal don't forget to make the view fully transparent at the beginning nightView.alpha = 0;. – Floris497 Mar 03 '12 at 22:27
  • 1
    This solution does not darken the image but the UIImageView. If you are looking for a code that darkens the image look at my answer. – Rafał Sroka Aug 04 '12 at 12:00