I managed to get a UIView in greyscale by adding the following view on top:
@interface GreyscaleAllView : UIView
@property (nonatomic, retain) UIView *underlyingView;
@end
@implementation GreyscaleAllView
@synthesize underlyingView;
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// draw the image
[self.underlyingView.layer renderInContext:context];
// set the blend mode and draw rectangle on top of image
CGContextSetBlendMode(context, kCGBlendModeColor);
CGContextSetRGBFillColor(context, 0.0, 0.0, 0.0, 1.0);
CGContextFillRect(context, rect);
[super drawRect:rect];
}
@end
It works, but the content doesn't get updated unless i manually call setNeedsDisplay. (I can press a UIButton and the action fires, but nothing changes in appearance) So for it to behave like expected I call setNeedsDisplay 60 times each second. What am I doing wrong?
UPDATE:
The viewcontroller inits the overlayview with this:
- (void)viewDidLoad
{
[super viewDidLoad];
GreyscaleAllView *grey = [[[GreyscaleAllView alloc] initWithFrame:self.view.frame] autorelease];
grey.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
grey.userInteractionEnabled = NO;
[self.view addSubview:grey];
}
I've added this for the underlaying views to redraw:
@implementation GreyscaleAllView
- (void)setup {
self.userInteractionEnabled = FALSE;
self.contentMode = UIViewContentModeRedraw;
[self redrawAfter:1.0 / 60.0 repeat:YES];
}
- (void)redrawAfter:(NSTimeInterval)time repeat:(BOOL)repeat {
if(repeat) {
// NOTE: retains self - should use a proxy when finished
[NSTimer scheduledTimerWithTimeInterval:time target:self selector:@selector(setNeedsDisplay) userInfo:nil repeats:YES];
}
[self setNeedsDisplay];
}
What you really want is for the subview to only be told to redraw when the parent view redraws, in which case you can override -setNeedsDisplay
in the parent view and make it call -setNeedsDisplay
on the GreyscaleAllView
. This requires subclassing UIView for your UIViewController
's view
property.
i.e. implement loadView
in the controller, and set
self.view = [[CustomView alloc] initWithFrame:frame];
where CustomView overrides setNeedsDisplay
as described above:
@implementation CustomView
- (void)setNeedsDisplay
{
[grayView setNeedsDisplay]; // grayView is a pointer to your GreyscaleAllView
[super setNeedsDisplay];
}
@end
For completeness, you should also do the same for -setNeedsDisplayInRect:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With