Mega Gold Challenge


#1

I’m pondering the Gold Challenge and have a question about what your interpretation is of Joe’s panel? Is this a new UIView? Honestly, I’m not to do it any other way. I was thinking of using code in drawRect but wasn’t sure how to implement a selection for that. I just don’t want to look at someone else’s solution and spoil all of the fun.

Jeff


#2

I’ve decided to use a ViewController subclass with a xib file. I’m modifying the view to conform to a small panel with a few buttons associated with different colors. I believe the init method will have to include some code to position the view at the top, or wherever I need it.

I welcome any course corrections.

Jeff


#3

Changed my mind. I think it may be simpler just to add a subView to the existing view. How 'bout it?

jeff


#4

I have a solution to this challenge and will post my code when I am able to do so - working on iPad right now. In the mean time, here is the general idea of my code.

  1. Added a @property to Line.h called color so I could store the individual colors along with the coordinates.

  2. Added a new UIView subclass called ColorPicker to draw the panel and perform the color selection. The color selection is a public method that takes a CGPoint to determine the selected color. This method simply takes the x location from the point and uses an if then else statement to return the UIColor of the box selected. I know this probably isn’t very elegant as it is all hard coded. I wante something a little more dynamic. Perhaps later as I learn more.

  3. Inside of the tap method in TouchDrawView I use a combination of isHiden to determine if the panel is open and hitPoint: withEvent: to determine if the touch happened inside of the panel. If inside the panel the color selection method I mentioned above is called and if outside of the panel it closes the panel. If the panel is hidden when the touch happens I ignore the color selection code and the other code is executed.

  4. The last modification was just to add code to the existing code to set and retrieve the color of the lines. Pretty easy since I used a property of the line.


#5

Here’s the code for my solution. I included all of the code for TouchDrawView even though I did not change anything in some of the methods.

Line.h

@interface Line : NSObject

@property(nonatomic)CGPoint begin;
@property(nonatomic)CGPoint end;
@property(nonatomic)UIColor *color;

@end

ColorPicker.h

@interface ColorPicker : UIView

-(UIColor *)selectColorFromTappedPoint:(CGPoint)tappedPoint;

@end

ColorPicker.m

#import "ColorPicker.h"

@implementation ColorPicker

-(id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    
    return self;
}

-(UIColor *)selectColorFromTappedPoint:(CGPoint)tappedPoint
{
    UIColor *color;
    float x = tappedPoint.x;
    float quadrant = [[UIScreen mainScreen] bounds].size.width/4.0;
    
    if (x < quadrant)
    {
        color = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.75];
    }
    else if (x > quadrant && x < quadrant*2)
    {
        color = [UIColor colorWithRed:0.0 green:0.0 blue:1.0 alpha:0.75];
    }
    else if (x > quadrant*2 && x < quadrant*3)
    {
        color = [UIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:0.75];
    }
    else
    {
        color = [UIColor colorWithRed:1.0 green:1.0 blue:0.0 alpha:0.75];
    }
    
    return color;
}



-(void)drawRect:(CGRect)rect
{
    //Get Context
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSetLineWidth(context, 2);
    CGContextSetLineCap(context, kCGLineCapRound);
    
//    CGContextMoveToPoint(context, 0, 0);
//    CGRect rectangle = CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, 100);
//    CGContextAddRect(context, rectangle);
//    CGContextStrokePath(context);
    
    //Draw 4 rectangles each with different color
    CGRect rectangle;
    CGFloat width = [[UIScreen mainScreen] bounds].size.width/4.0;
    
    //Rectangle #1
    rectangle = CGRectMake(0, 0, width, 100);
    CGContextAddRect(context, rectangle);
    [[UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.75] setFill];
    CGContextStrokePath(context);
    CGContextFillRect(context, rectangle);
    
    //Rectangle #2
    rectangle = CGRectMake(width, 0, width, 100);
    CGContextAddRect(context, rectangle);
    [[UIColor colorWithRed:0.0 green:0.0 blue:1.0 alpha:0.75] setFill];
    CGContextStrokePath(context);
    CGContextFillRect(context, rectangle);
    
    //Rectangle #3
    rectangle = CGRectMake(width*2.0, 0, width, 100);
    CGContextAddRect(context, rectangle);
    [[UIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:0.75] setFill];
    CGContextStrokePath(context);
    CGContextFillRect(context, rectangle);
    
    //Rectangle #4
    rectangle = CGRectMake(width*3.0, 0, width, 100);
    CGContextAddRect(context, rectangle);
    [[UIColor colorWithRed:1.0 green:1.0 blue:0.0 alpha:0.75] setFill];
    CGContextStrokePath(context);
    CGContextFillRect(context, rectangle);

}
@end

TouchDrawView.m

#import "TouchDrawView.h"
#import "Line.h"
#import "ColorPicker.h"

@implementation TouchDrawView
{
    ColorPicker *cpView;
    UIColor *lineColor;
}

-(id)initWithFrame:(CGRect)r
{
    self = [super initWithFrame:r];
    
    //Add the colorpicker view then set it to hidden
    CGFloat colorPickerWidth = [[UIScreen mainScreen] bounds].size.width;
    CGFloat colorPickerHieght = 100;
    CGRect colorRect = CGRectMake(0, 0, colorPickerWidth, colorPickerHieght);
    cpView = [[ColorPicker alloc] initWithFrame:colorRect];
    [self addSubview:cpView];
    [cpView setHidden:YES];

    
    if (self)
    {
        //Set default line color
        lineColor = [UIColor colorWithRed:0.7 green:0.7 blue:1 alpha:0.8];
        
        linesInProcess = [[NSMutableDictionary alloc] init];
        completeLines  = [[NSMutableArray alloc] init];
        
        [self setBackgroundColor:[UIColor whiteColor]];
        [self setMultipleTouchEnabled:YES];
        
        UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];
        [tapRecognizer setNumberOfTapsRequired:1];
        [self addGestureRecognizer:tapRecognizer];
        
        UITapGestureRecognizer *dblTapReconizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clearAll:)];
        [dblTapReconizer setNumberOfTapsRequired:2];
        [self addGestureRecognizer:dblTapReconizer];
        
        UISwipeGestureRecognizer *threeFingerSwipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(selectColor:)];
        [threeFingerSwipe setNumberOfTouchesRequired:2];
        [threeFingerSwipe setDirection:UISwipeGestureRecognizerDirectionUp];
        [threeFingerSwipe cancelsTouchesInView];
        [self addGestureRecognizer:threeFingerSwipe];
                
        UILongPressGestureRecognizer *pressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
        [self addGestureRecognizer:pressRecognizer];
        
        moveRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(moveLine:)];
        [moveRecognizer setDelegate:self];
        [moveRecognizer setCancelsTouchesInView:NO];
        [self addGestureRecognizer:moveRecognizer];
    }
    
    return self;
}

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)other
{
    if (gestureRecognizer == moveRecognizer) return YES;
    
    return NO;
}

-(void)selectColor:(UISwipeGestureRecognizer *)gr
{
    [linesInProcess removeAllObjects];
    [cpView setHidden:NO];    
}

-(void)moveLine:(UIPanGestureRecognizer *)gr
{
    if (![self selectedLine]) return;
    
    if ([gr state] == UIGestureRecognizerStateChanged)
    {
        CGPoint translation = [gr translationInView:self];
        
        CGPoint begin = [[self selectedLine] begin];
        CGPoint end   = [[self selectedLine] end];
        
        begin.x += translation.x;
        begin.y += translation.y;
        end.x   += translation.x;
        end.y   += translation.y;
        
        [[self selectedLine] setBegin:begin];
        [[self selectedLine] setEnd:end];
        
        [self setNeedsDisplay];
        
        [gr setTranslation:CGPointZero inView:self];
    }
}

-(void)tap:(UIGestureRecognizer *)gr
{
    CGPoint tappedPoint = [gr locationInView:self];
    if (![cpView isHidden])
    {
        if ([cpView hitTest:tappedPoint withEvent:nil])
        {
            NSLog(@"touched inside of cpview");
            lineColor = [cpView selectColorFromTappedPoint:tappedPoint];
            NSLog(@"selected color is %@", [lineColor description]);
        }
        else
        {
            [cpView setHidden:YES];
            NSLog(@"did not touch inside cpview");
        }
    }
//    else
//    {
//        NSLog(@"did not touch inside cpview");
//    }
    
    [self setSelectedLine:[self lineAtPoint:tappedPoint]];
    
    if ([self selectedLine])
    {
        [self becomeFirstResponder];
        
        UIMenuController *menu = [UIMenuController sharedMenuController];
        UIMenuItem *deleteItem = [[UIMenuItem alloc] initWithTitle:@"Delete" action:@selector(deleteLine:)];
        [menu setMenuItems:[NSArray arrayWithObject:deleteItem]];
        [menu setTargetRect:CGRectMake(tappedPoint.x, tappedPoint.y, 20, 20) inView:self];
        [menu setMenuVisible:YES animated:YES];
    }
    else
    {
        [[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES];
    }
    
    [linesInProcess removeAllObjects];
    [self setNeedsDisplay];
}

-(void)longPress:(UIGestureRecognizer *)gr
{
    if ([gr state] == UIGestureRecognizerStateBegan)
    {
        CGPoint point = [gr locationInView:self];
        [self setSelectedLine:[self lineAtPoint:point]];
        
        if ([self selectedLine])
        {
            [linesInProcess removeAllObjects];
        }
    }
    else if([gr state] == UIGestureRecognizerStateEnded)
    {
        [self setSelectedLine:nil];
    }
    
    [self setNeedsDisplay];
}

-(BOOL)canBecomeFirstResponder
{
    return YES;
}

-(void)deleteLine:(id)sender
{
    [completeLines removeObject:[self selectedLine]];
    
    [self setNeedsDisplay];
}

-(Line *)lineAtPoint:(CGPoint)p
{
    for (Line *l in completeLines)
    {
        CGPoint start = [l begin];
        CGPoint end   = [l end];
        
        for (float t = 0.0; t <= 1.0; t += 0.05)
        {
            float x = start.x + t*(end.x - start.x);
            float y = start.y + t*(end.y - start.y);
            
            if (hypot(x - p.x, y - p.y) < 20.0)
            {
                return l;
            }
        }
    }
    
    return nil;
}

-(void)touchesBegan:(NSSet *)touches
          withEvent:(UIEvent *)event
{
    for (UITouch *t in touches)
    {
//        if ([t tapCount] > 1)
//        {
//            [self clearAll];
//            return;
//        }
        
        //Use touch object packed in a nsvalue as key
        NSValue *key = [NSValue valueWithNonretainedObject:t];
        
        //Create a line for the value
        CGPoint loc = [t locationInView:self];
        Line *newLine = [[Line alloc] init];
        [newLine setBegin:loc];
        [newLine setEnd:loc];
        [newLine setColor:lineColor];
        
        [linesInProcess setObject:newLine forKey:key];
    }
}

-(void)touchesMoved:(NSSet *)touches
          withEvent:(UIEvent *)event
{
    for (UITouch *t in touches)
    {
        //Get the key then value fron the touch event
        NSValue *key = [NSValue valueWithNonretainedObject:t];
        Line *line = [linesInProcess objectForKey:key];
        
        //Update the end point
        CGPoint loc = [t locationInView:self];
        [line setEnd:loc];
    }
    
    [self setNeedsDisplay];
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self endTouches:touches];
}

-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self endTouches:touches];
}

-(void)endTouches:(NSSet *)touches
{
    for (UITouch *t in touches)
    {
        NSValue *key = [NSValue valueWithNonretainedObject:t];
        Line *line = [linesInProcess objectForKey:key];
        
        if (line)
        {
            [completeLines addObject:line];
            [linesInProcess removeObjectForKey:key];
        }
    }
    
    [self setNeedsDisplay];
}

-(void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 10.0);
    CGContextSetLineCap(context, kCGLineCapRound);
    
    //Draw Complete Lines in Black
//    [[UIColor blackColor] set];
    for (Line *line in completeLines)
    {
        [[line color] set];
        CGContextMoveToPoint(context, [line begin].x, [line begin].y);
        CGContextAddLineToPoint(context, [line end].x, [line end].y);
        CGContextStrokePath(context);
    }
    
    //Draw Lines in Process in Red
    [[UIColor redColor] set];
    for (NSValue *v in linesInProcess)
    {
        Line *line = [linesInProcess objectForKey:v];
        CGContextMoveToPoint(context, [line begin].x, [line begin].y);
        CGContextAddLineToPoint(context, [line end].x, [line end].y);
        CGContextStrokePath(context);
    }
    
    if ([self selectedLine])
    {
        [[UIColor greenColor] set];
        CGContextMoveToPoint(context, [[self selectedLine] begin].x, [[self selectedLine] begin].y);
        CGContextAddLineToPoint(context, [[self selectedLine] end].x, [[self selectedLine] end].y);
        CGContextStrokePath(context);
    }
}

-(void)clearAll:(UIGestureRecognizer *)gr
{
    [linesInProcess removeAllObjects];
    [completeLines removeAllObjects];
    
    [self setNeedsDisplay];
}

@end

#6

Hi Jeff,

I believe your approach is indeed correct. We should create a custom UIView with the color picking controls, either programmatically like you did, or using the Interface Builder like I did.

Apple docs, as I just learned, state that there is no need for a 1:1 parity between view controllers and views, like I used to think. Instead a view controller should be responsible for a complete view hierarchy, which is really handy in situations just like this.
developer.apple.com/library/pre … Views.html

That’s the main difference between your solution and mine. I used IB to place a few UIButtons next to each other, and set different background colors to each of them. All buttons then trigger the same action -changeColor, and all the method implementation does is to set the line color to the background color of the button that was clicked, which you can obtain using [sender backgroundColor].

The most difficult part, in my case, was to understand how to add a subview straight from the xib file I created. If anyone is curious, this article describes how do to it: stackoverflow.com/questions/5354 … ew-what-am

A few comments about your code:

  1. Your threeFingerSwipe gesture recognizer should require 3 taps, but it’s currently requiring just 2. I imagine you did this intentionally to be able to test it on the simulator, which is clever.

  2. [threeFingerSwipe cancelsTouchesInView] should instead be [threeFingerSwipe setCancelsTouchesInView:YES];

  3. In my solution, when I detect a 3 finger swipe up, I’m first checking to see if the color panel is already being displayed, and hide it if it is. I’m also dismissing the color panel when the user selects a color, so that it doesn’t get in the way when drawing.

My solution:

//  TouchDrawView.m
//  TouchTracker
//
//  Created by Gilmar Lira on 7/30/13.
//  Copyright (c) 2013 com.bignerdranch. All rights reserved.
//

#import "TouchDrawView.h"
#import "LineStore.h"
#import "Line.h"
#import "ColorViewController.h"

@implementation TouchDrawView
@synthesize selectedLine, speed, color;


- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    
    if (self) {
        linesInProgress = [[NSMutableDictionary alloc] init];
        
        [self setBackgroundColor:[UIColor whiteColor]];
        
        [self setMultipleTouchEnabled:YES];
        
        UISwipeGestureRecognizer *showColors = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(showColors:)];
        [showColors setDirection:UISwipeGestureRecognizerDirectionUp];
        [showColors setNumberOfTouchesRequired:3];
        [self addGestureRecognizer:showColors];
        
        
        UITapGestureRecognizer *clearRecog = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clearAll:)];
        [clearRecog setNumberOfTapsRequired:3];
        [self addGestureRecognizer:clearRecog];
        
        UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];
        [tapRecognizer setCancelsTouchesInView:YES];
        [self addGestureRecognizer:tapRecognizer];
        
        UILongPressGestureRecognizer *pressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(press:)];
        [self addGestureRecognizer:pressRecognizer];
        
        moveRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(moveLine:)];
        [moveRecognizer setDelegate:self];
        [moveRecognizer setCancelsTouchesInView:NO];
        [self addGestureRecognizer:moveRecognizer];
        
        NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"ColorViewController" owner:self options:nil];
        colorPicker = [subviewArray objectAtIndex:0];
        
        [colorPicker setFrame:CGRectMake(280, 600, 464, 100)];
        
        [self addSubview:colorPicker];
        
        [colorPicker setHidden:YES];


    }
    return self;
}


- (void)changeColor:(id)sender
{
    UIColor *c = [sender backgroundColor];
    [self setColor:c];
    [colorPicker setHidden:YES];
}

#pragma mark Gesture Recognizers

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    
    if (gestureRecognizer == moveRecognizer) {
        return YES;
    }
    
    return NO;
}


- (BOOL)canBecomeFirstResponder
{
    return YES;
}


- (void)showColors:(UISwipeGestureRecognizer *)s
{
    NSLog(@"3 finger swipe up recognized.");
    
    [self setSelectedLine:nil];
    
    [[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES];
    
    [linesInProgress removeAllObjects];
    
    
    if ([colorPicker isHidden]) {
        [colorPicker setHidden:NO];
    } else {
        [colorPicker setHidden:YES];
    }
    
}


- (void)clearAll:(UIGestureRecognizer *)gr
{
    [linesInProgress removeAllObjects];
    [[LineStore sharedStore] removeAllLines];
    
    [self setNeedsDisplay];
}



- (void)moveLine:(UIPanGestureRecognizer *)gr
{
    // If we haven't selected a line, we don't do anything here
    
    // When the pan recognizer changes its position...
    if ([gr state] == UIGestureRecognizerStateChanged) {
        if (![self selectedLine]) {
            
            [self setSpeed:[gr velocityInView:self]];
            
        } else {

            // How far has the pan moved?
            CGPoint translation = [gr translationInView:self];
            
            // Add the translation to the current begin and end points of the line
            CGPoint begin = [[self selectedLine] begin];
            CGPoint end = [[self selectedLine] end];
            begin.x += translation.x;
            begin.y += translation.y;
            end.x += translation.x;
            end.y += translation.y;
            
            // Set the new beginning and end points of the line
            [[self selectedLine] setBegin:begin];
            [[self selectedLine] setEnd:end];
            
            [self setNeedsDisplay];
            [gr setTranslation:CGPointZero inView:self];
        }
    }
}


- (void)press:(UIGestureRecognizer *)gr
{
    if ([gr state] == UIGestureRecognizerStateBegan) {
        CGPoint point = [gr locationInView:self];
        [self setSelectedLine:[self lineAtPoint:point]];
        
        if ([self selectedLine]) {
            [linesInProgress removeAllObjects];
            
        }
        
        
    } else if ([gr state] == UIGestureRecognizerStateEnded) {
        [self setSelectedLine:nil];
    }
    
    [self setNeedsDisplay];
}


- (void)tap:(UIGestureRecognizer *)gr
{
    NSLog(@"Recognized tap.");
    
    CGPoint point = [gr locationInView:self];
    
    if ([self lineAtPoint:point] == [self selectedLine]) {
        NSLog(@"Same line.");
        return;
    }
    
    [self setSelectedLine:[self lineAtPoint:point]];
    
    // If we just tapped, remove all lines in process so that a tap doesn't result in a new line
    [linesInProgress removeAllObjects];
    
    if ([self selectedLine]) {
        // We'll talk about this shortly
        [self becomeFirstResponder];
        
        // Grab the menu controller
        UIMenuController *menu = [UIMenuController sharedMenuController];
        
        // Create a new "Delete" UIMenuItem
        UIMenuItem *deleteItem = [[UIMenuItem alloc] initWithTitle:@"Delete" action:@selector(deleteLine:)];
        
        [menu setMenuItems:[NSArray arrayWithObject:deleteItem]];
        
        // Thell the menu where it should come from and show it
        [menu setTargetRect:CGRectMake(point.x, point.y, 2, 2) inView:self];
        [menu setMenuVisible:YES];
    } else {
        // Hide the menu if no line is selected
        [[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES];
    }
    
    [self setNeedsDisplay];
    
}


- (void)deleteLine:(id)sender
{
    [[LineStore sharedStore] removeLine:[self selectedLine]];
    
    [self setNeedsDisplay];
}


- (Line *)lineAtPoint:(CGPoint)p
{
    // Find a line close to p
    for (Line *l in [[LineStore sharedStore] allLines]) {
        CGPoint start = [l begin];
        CGPoint end = [l end];
        
        // Check a few points of the line
        for (float t = 0.0; t <= 1.0; t +=0.05) {
            
            float x = start.x + t * (end.x - start.x);
            float y = start.y + t * (end.y - start.y);
            
            // If the tapped point is within 20 points, let's return this line
            if (hypot(x - p.x, y - p.y) < 20.0) {
                return l;
            }
            
        }
        
    }
    
    // If nothing is close enough to the tapped point, then we didn't select a line
    return nil;
}


#pragma mark UITouch events


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    for (UITouch *t in touches) {
        // Is there any line currently being edited?
        if ([self selectedLine]) {
            selectedLine = nil;
            [[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES];
        }
        
        // User the touch object (packed in a NSValue as the key
        NSValue *key = [NSValue valueWithNonretainedObject:t];
        
        // Create a line for the value
        CGPoint loc = [t locationInView:self];
        Line *newLine = [[Line alloc] init];
        [newLine setBegin:loc];
        [newLine setEnd:loc];
        [newLine setSpeed:[self speed]];
        [newLine setColor:[self color]];
        
        // Put pair in dictionary
        [linesInProgress setObject:newLine forKey:key];
    }
}


- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    // Update linesInProcess with moved touches
    for (UITouch *t in touches) {
        NSValue *key = [NSValue valueWithNonretainedObject:t];
        
        // Find the line for this touch
        Line *line = [linesInProgress objectForKey:key];
        
        // Update the line
        CGPoint loc = [t locationInView:self];
        [line setEnd:loc];
        [line setSpeed:[self speed]];
    }
    [self setNeedsDisplay];
}


- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self endTouches:touches];
}


- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self endTouches:touches];
}


- (void)endTouches:(NSSet *)touches
{
    for (UITouch *t in touches) {
        NSValue *key = [NSValue valueWithNonretainedObject:t];
        
        Line *line = [linesInProgress objectForKey:key];
        
        if (line) {
            [[LineStore sharedStore] addLine:line];
            [linesInProgress removeObjectForKey:key];
        }
        
    }
    [self setNeedsDisplay];
}


#pragma mark drawRect

- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSetLineCap(context, kCGLineCapRound);
    
    for (Line *line in [[LineStore sharedStore] allLines]) {
        CGContextMoveToPoint(context, [line begin].x, [line begin].y);
        CGContextAddLineToPoint(context, [line end].x, [line end].y);
        
        float angle = atan2f( ([line begin].y - [line end].y), ([line end].x - [line begin].x) ) * 180/M_PI;
        
        if ((([line end].x - [line begin].x) < 0 && ([line begin].y - [line end].y) < 0) ||
            (([line end].x - [line begin].x) >= 0 && ([line begin].y - [line end].y) < 0) ) {
            angle = angle + 360;
        }

        if (![line color]) {
            [[UIColor colorWithHue:angle/360 saturation:1.0 brightness:1.0 alpha:1.0] setStroke];
        } else {
            [[line color] setStroke];
        }
        
        float savedSpeed = 1 + abs( ([line speed].x + [line speed].y ) / 2 ) / 20;
        
        CGContextSetLineWidth(context, savedSpeed);
        CGContextStrokePath(context);
    }
    

    for (NSValue *v in linesInProgress) {
        Line *line = [linesInProgress objectForKey:v];
        CGContextMoveToPoint(context, [line begin].x, [line begin].y);
        CGContextAddLineToPoint(context, [line end].x, [line end].y);
        
        float angle = atan2f( ([line begin].y - [line end].y), ([line end].x - [line begin].x) ) * 180/M_PI;
        
        if ( (([line end].x - [line begin].x) < 0 && ([line begin].y - [line end].y) < 0) || (([line end].x - [line begin].x) >= 0 && ([line begin].y - [line end].y) < 0) ) {
            angle = angle+360;
        }
        
        if (![self color]) {
            [[UIColor colorWithHue:angle/360 saturation:1.0 brightness:1.0 alpha:1.0] setStroke];
        } else {
            [[self color] setStroke];
        }
        
        float currentSpeed = 1 + abs( ( [self speed].x + [self speed].y ) / 2 ) / 20;
        
        CGContextSetLineWidth(context, currentSpeed);

        CGContextStrokePath(context);
    }

    
    // If there is a selected line, draw it
    if ([self selectedLine]) {
        [[UIColor blackColor] set];
        
        CGContextMoveToPoint(context, [[self selectedLine] begin].x, [[self selectedLine] begin].y);
        CGContextAddLineToPoint(context, [[self selectedLine] end].x, [[self selectedLine] end].y);
        CGContextStrokePath(context);
    }
}



@end

#7

Thanks, Plastic. In response to your point:

  1. Yes, you are correct. I used two finger because I’m only using the simulator for now.

2). I’ll Check on that. Thanks.

3). I believe my code does the same. I think.