Bronze Solution #2


#1

So here’s my solution using a LineStore. Seems to add a bit of simplicity to the storage model rather than bundling everything with the view file. Just doesn’t seem right to have the view do so much, even with the switch to the store model. No changes to the Line model or the controller code. Major changes from #1 are addition of LineStore and removal of completeLines array in TouchDrawView.

LineStore.h

@class Line;

@interface LineStore : NSObject
{
    NSMutableArray *completeLines;
}

+(LineStore *)lineStore;


-(NSMutableArray *)completeLines;

-(Line *)createLine;
-(void)addLineToCompleteLines:(Line *)l;
-(void)removeAllLines;

-(NSString *)lineArchivePath;
-(BOOL)saveLines;

@end

LineStore.m

#import "LineStore.h"
#import "Line.h"

@implementation LineStore
//This will only save completed lines. Will work on inprocess next

-(id)init
{
    self = [super init];
    
    if (self)
    {
        // Load lines from the archive
        NSString *path = [self lineArchivePath];
        completeLines = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
        
        if (!completeLines)
        {
            completeLines = [[NSMutableArray alloc] init];
        }
    }
    
    return self;
}

+(LineStore *)lineStore
{
    static LineStore *lineStore = nil;
    if (!lineStore)
    {
        lineStore = [[super allocWithZone:nil] init];
    }
    
    return lineStore;
}

+(id)allocWithZone:(NSZone *)zone
{
    return [self lineStore];
}

-(NSMutableArray *)completeLines
{
    return completeLines;
}

-(Line *)createLine
{
    Line *line = [[Line alloc] init];
    
    return line;
}

-(void)addLineToCompleteLines:(Line *)l
{
    [completeLines addObject:l];
}

-(void)removeAllLines
{
    [completeLines removeAllObjects];
}

-(NSString *)lineArchivePath
{
    NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString * documentDirectory = [documentDirectories objectAtIndex:0];
    
    return [documentDirectory stringByAppendingPathComponent:@"lines.archive"];
}

-(BOOL)saveLines
{
    NSString *savePath = [self lineArchivePath];
    
    return [NSKeyedArchiver archiveRootObject:completeLines toFile];
}

@end

TouchDrawView.h

@interface TouchDrawView : UIView

-(void)clearAll;
-(void)endTouches:(NSSet *)touches;

@end

TouchDrawView.m

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

@interface TouchDrawView ()
{
    NSMutableDictionary *linesInProcess;
//    NSMutableArray *completeLines;
}
@end


@implementation TouchDrawView

-(id)initWithFrame:(CGRect)r
{
    self = [super initWithFrame:r];
    
    if (self)
    {
        linesInProcess = [[NSMutableDictionary alloc] init];
//        completeLines = [[LineStore lineStore] completeLines];
        
        [self setBackgroundColor:[UIColor whiteColor]];
        [self setMultipleTouchEnabled:YES];
    }
    
    return self;
}

-(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 = [[LineStore lineStore] createLine];
        [newLine setBegin:loc];
        [newLine setEnd:loc];
        
        [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)
        {
            [[LineStore lineStore] addLineToCompleteLines: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 [[LineStore lineStore] completeLines])
    {
        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);
    }
}

-(void)clearAll
{
    [linesInProcess removeAllObjects];
    [[LineStore lineStore] removeAllLines];
    
    [self setNeedsDisplay];
}

@end

AppDelegate.m

#import "AppDelegate.h"
#import "TouchViewController.h"
#import "LineStore.h"

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    
    [[self window] setRootViewController:[[TouchViewController alloc] init]];
    
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    
    //Archive the line data
    //TouchDrawView *tvView = [tvc tvView];
    BOOL success = [[LineStore lineStore] saveLines];
    
    if (success)
    {
        NSLog(@"Lines were saved");
    }
    else
    {
        NSLog(@"Lines were NOT saved");
    }    
}