Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIView in greyscale. View not being redrawn

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];
}
like image 515
jalmaas Avatar asked Nov 13 '22 05:11

jalmaas


1 Answers

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:

like image 79
yfrancis Avatar answered Jan 01 '23 02:01

yfrancis