Calling drawRect from within HypnosisView


#1

After doing the bronze challenge with randomly-colored circles, I wanted to tweak the app such that shaking would cause a new set of random colors to appear.

I thought I could have the method motionBegan: withEvent: call the method drawRect: with an appropriate argument for CGRect and send [self setNeedsDisplay];

When I did this, I saw the desired result (the colors change), but received an error for each circle drawn:
: CGContextSetLineWidth: invalid context 0x0

Since ctx is defined as the current drawing context, why is it coming up as invalid?

Your help will be appreciated!

My tweaked version of the BNR code:

#import "HypnosisView.h"

@implementation HypnosisView
// @synthesize circleColor;


- (BOOL)canBecomeFirstResponder
{
    return YES;
}

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    if (motion == UIEventSubtypeMotionShake) {
    NSLog(@"Device shaken.");
        CGRect box = [self bounds];
            
        [self drawRect:box];
        [self setNeedsDisplay];
        
        } 
}

- (void)drawRect:(CGRect)dirtyRect

{
    NSLog(@"HypnosisView - DrawRect called");
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGRect bounds = [self bounds];
    CGPoint center;
    center.x = bounds.origin.x + bounds.size.width /2.0;
    center.y = bounds.origin.y + bounds.size.height/2.0;
    NSLog(@"bounds x:%f, y:%f",center.x, center.y);
    
    // the radius of the circle should be nearly as big as the view
    float maxRadius = hypot(bounds.size.width, bounds.size.height) /2.0;
    
    // the thickness of the line should be 10 points wide:
    CGContextSetLineWidth(ctx, 10);

    // add shadow to subsequent drawing
    CGSize offset = CGSizeMake(3,2);
    CGColorRef color = [[UIColor darkGrayColor] CGColor];
    CGContextSetShadowWithColor(ctx, offset, 2.0, color);
    
        
    for (float currentRadius = maxRadius; currentRadius > 0; currentRadius -= 20)
    {
        CGFloat red = arc4random() % 10000001 * 0.0000001;
        CGFloat green = arc4random() % 10000001 * 0.0000001;
        CGFloat blue = arc4random() % 10000001 * 0.0000001;
    
        // NSLog(@"RGB: %f, %f, %f", red, green, blue);
        [[UIColor colorWithRed:red green:green blue:blue alpha:1.0] setStroke];
         CGContextAddArc(ctx, center.x, center.y, currentRadius, 0.0, M_PI * 2, YES);
        CGContextStrokePath(ctx);
    }

   /*  // Add the text
    NSString *text = @"You are getting sleepy.";
    //UIFont *font = [UIFont boldSystemFontOfSize:28];
    CGRect textRect;
    textRect.size = [text sizeWithFont:font];
    textRect.origin.x = center.x - textRect.size.width /2.0;
    textRect.origin.y = center.y - textRect.size.height /2.0;
    
    [[UIColor blackColor] setFill];
    CGSize offset = CGSizeMake(4, 3);
    CGColorRef color = [[UIColor darkGrayColor] CGColor];
    
    CGContextSetShadowWithColor(ctx, offset, 2.0, color);
    [text drawInRect:textRect withFont:font];
    */

}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setBackgroundColor:[UIColor clearColor]];
        // [self setCircleColor:[UIColor greenColor]];
    }
    return self;
}

@end

#2

I’ll try to answer my own question – one is not supposed to call drawRect directly, and I’m guessing that because I’m calling it directly, the appropriate drawing context is not available.

I tried using [setNeedsDisplay], but since that seems to make the display change only once – I haven’t figured out how to call it repeatedly and have the colors flicker through multiple redraws for a few seconds to get the “hypnotic” effect I was going for. [view display] may do it.