Gold Challenge Solution


#1

This one was a bit painful especially as I tried to interpret what they wanted in terms of functionality. When you think making circles with two fingers you think a pinch like gesture but we aren’t there yet and the instructions indicated the fingers should be representing “corners of the bounding box”. As such I went about this in terms of a two finger gesture with each finger representing a corner of a rectangle that the circle should fit within. This is single gesture unlike the lines where we have continuing progress as the finger is dragged across the screen. As such there is no circle in progress like there is with line. The challenge mentions dictionary which has me concerned that I didn’t interpret this 100% correctly as I found a holding array sufficient for the circles. Again that is because there is no “circle in progress”.

I first created a new class to represent the two points (i.e. the two finger locations) that are the bounds for the circle.

BNRCircleView.h

#import <Foundation/Foundation.h>

@interface BNRCircle : NSObject

@property (nonatomic) CGPoint point1;
@property (nonatomic) CGPoint point2;

@end

Everything else is within BNRDrawView.m

I added one property to the local interface to house the circles.

...
@property (nonatomic, strong) NSMutableArray *finishedCircles;
...

I instantiated the array within the init method:

...
self.finishedCircles = [[NSMutableArray alloc]init];
...

I created a method to compute the circle and to draw it within a rectangular space.

-(void)strokeCircle:(BNRCircle *)circle
{
    float w = fabs(circle.point1.x-circle.point2.x);
    float h = fabs(circle.point1.y-circle.point2.y);
    
    CGRect r = CGRectMake(circle.point1.x, circle.point1.y, w, h);
    
    UIBezierPath *bp = [UIBezierPath bezierPathWithOvalInRect:r];
    bp.lineWidth = 10;
    
    [bp stroke];
}

I updated the drawRect method to loop through the finishedCircles:

-(void)drawRect:(CGRect)rect
{
    for (BNRLine *line in self.finishedLines)
    {
        float xDiff = line.end.x - line.begin.x;
        float yDiff = line.end.y - line.begin.y;
        float angle = fabs(atan2(yDiff,xDiff) * (180 / M_PI));
        float colorSet = fabs((angle-100.0)/100.0);
        
        [[UIColor colorWithHue:colorSet saturation:1.0 brightness:1.0 alpha:1.0]set];
        [self strokeLine:line];
    }
    
    for (BNRCircle *circle in self.finishedCircles)
    {
        [self strokeCircle:circle];
    }
    
    [[UIColor redColor]set];
    
    for (NSValue *key in self.linesInProgress)
    {
        [self strokeLine:self.linesInProgress[key]];
    }
}

I didn’t care about touchesBegan or touchesMoved as again I am just taking the two finger points. Therefore I only cared about touchesEnded. Here I check whether the gesture was with two fingers and if so I assume a circle is wanted. If anything other than 2 fingers is detected I assume lines. Knowing that touches will contain two points if two fingers are detected, I directly go to the indexes of each touch item to get its location. I cannot do that within the NSSet so I move the set to an array and get it from that.

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    BNRLine *line;
   
    NSLog(@"%@",NSStringFromSelector(_cmd));
    
    if ([touches count] != 2)
    {
       for (UITouch *t in touches)
       {
           NSValue *key = [NSValue valueWithNonretainedObject:t];
           line = self.linesInProgress[key];
        
           if(line)[self.finishedLines addObject:line];
        
           [self.linesInProgress removeObjectForKey:key];
       }
    }
    else
    {
        NSArray *touchArray = [touches allObjects];
        UITouch *t1 = [touchArray objectAtIndex:0];
        UITouch *t2 = [touchArray objectAtIndex:1];
        BNRCircle *circle = [[BNRCircle alloc]init];
        
        circle.point1 = [t1 locationInView:self];
        circle.point2 = [t2 locationInView:self];
        
        if(circle) [self.finishedCircles addObject:circle];
    }
    
    [self setNeedsDisplay];
}