Book doesnt mention what Xcode is suggesting. Error


#1

My app is not allowing me to draw on my screen. When I touch the screen to draw (on the simulator) I see a red dot in the top right corner, and when I let go it turns black. Those are the colors we coded to use when touched and the line color when drawn however it will not move from the top corner when I move my finger, nor will it start drawing where I place my finger in the first place.

My code looks exactly like the book beside two areas, which when I copied out of the book, id get an error but when I did what Xcode suggested when I got that error, the error went away. I’m wondering if these Xcode suggested changes are whats giving me problems.

Below its is suggesting I put a pointer * to the left of *[line begin] and *[line end], but in the book they do not use the * next to [line begin] and [line end].

[code]- (void)drawRect:(CGRect)rect
{
// Create a block that will configure and draw a BNRLine
void (^strokeLine)(BNRLine *) = ^(BNRLine line) {
UIBezierPath bp = [UIBezierPath bezierPath];
[bp moveToPoint:
[line begin]];
[bp addLineToPoint:
[line end]];
[bp setLineWidth:10];
[bp setLineCapStyle:kCGLineCapRound];

    [bp stroke];
};

// Draw finished lines in black
[[UIColor blackColor] set];
for (BNRLine *line in _finishedLines) {
    strokeLine(line);
}
[[UIColor redColor] set];
for (NSValue *key in _linesInProgress) {
    strokeLine([_linesInProgress objectForKey:key]);
}

}
};[/code]
The other thing the code is suggesting is the & symbol to the left of &location in the 2 lines under where I allocation and initialize BNRLine. In the book it doesnt show a & symbol next to these.

[code]- (void)touchesBegan:(NSSet *)touches
withEvent:(UIEvent *)event
{
// Lets put in a log statement to see the order of events
NSLog(@"%@", NSStringFromSelector(_cmd));

for (UITouch *t in touches) {
    CGPoint location = [t locationInView:self];
    BNRLine *line = [[BNRLine alloc]init];
    [line setBegin:&location];
    [line setEnd:&location];
    
    NSValue *key = [NSValue valueWithNonretainedObject:t];
    
    [_linesInProgress setObject:line forKey:key];
                        
                        }

[self setNeedsDisplay];

}[/code]
I did try things on my own to solve this but you can only get so far when your new. Could this be what is not allowing me to draw as I should be? Thanks.


#2

Can you post all your code files, especially the file containing the type BNRLine?


#3

Sure thing, below should be everything associated with BNRLine, but let me know if my app delegates or or BNRDrawViewController (there is not much to them).

BNRDrawView.m

#import "BNRDrawView.h"
#import "BNRLine.h"

@interface BNRDrawView ()
{
    NSMutableDictionary *_linesInProgress;
    NSMutableArray *_finishedLines;
}
@end

@implementation BNRDrawView

-(id)initWithFrame:(CGRect)r
{
    self = [super initWithFrame:r];
    
    if (self) {
        _linesInProgress = [[NSMutableDictionary alloc]init];
        _finishedLines = [[NSMutableArray alloc]init];
        
        [self setBackgroundColor:[UIColor grayColor]];
        [self setMultipleTouchEnabled:YES];
    }
    return self;
}


- (void)drawRect:(CGRect)rect
{
    // Create a block that will configure and draw a BNRLine
    void (^strokeLine)(BNRLine *) = ^(BNRLine *line) {
        UIBezierPath *bp = [UIBezierPath bezierPath];
        [bp moveToPoint:*[line begin]];
        [bp addLineToPoint:*[line end]];
        [bp setLineWidth:10];
        [bp setLineCapStyle:kCGLineCapRound];
        
        [bp stroke];
    };
    
    // Draw finished lines in black
    [[UIColor blackColor] set];
    for (BNRLine *line in _finishedLines) {
        strokeLine(line);
    }
    [[UIColor redColor] set];
    for (NSValue *key in _linesInProgress) {
        strokeLine([_linesInProgress objectForKey:key]);
    }
}


- (void)touchesBegan:(NSSet *)touches
           withEvent:(UIEvent *)event
{
    // Lets put in a log statement to see the order of events
    NSLog(@"%@", NSStringFromSelector(_cmd));
    
    for (UITouch *t in touches) {
        CGPoint location = [t locationInView:self];
        BNRLine *line = [[BNRLine alloc]init];
        [line setBegin:&location];
        [line setEnd:&location];
        
        NSValue *key = [NSValue valueWithNonretainedObject:t];
        
        [_linesInProgress setObject:line forKey:key];
                            
                            }
    
    [self setNeedsDisplay];
}
    

- (void)touchesMoved:(NSSet *)touches
           withEvent:(UIEvent *)event
{
    
    // Put in a log statement to see the order of events
    NSLog(@"%@", NSStringFromSelector(_cmd));
    
    for (UITouch *t in touches) {
        NSValue *key = [NSValue valueWithNonretainedObject:t];
        BNRLine *line = [_linesInProgress objectForKey:key];
        
    }
    
    [self setNeedsDisplay];
}


- (void)touchesEnded:(NSSet *)touches
           withEvent:(UIEvent *)event
{
    NSLog(@"%@", NSStringFromSelector(_cmd));
    
    for (UITouch *t in touches) {
        NSValue *key = [NSValue valueWithNonretainedObject:t];
        BNRLine *line = [_linesInProgress objectForKey:key];
        
        
        [_finishedLines addObject:line];
        [_linesInProgress removeObjectForKey:key];
    }
    
    [self setNeedsDisplay];
}


- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    
    // Put in a log statement to see the order of events
    NSLog(@"%@", NSStringFromSelector(_cmd));
    
    for (UITouch *t in touches) {
        NSValue *key = [NSValue valueWithNonretainedObject:t];
        [_linesInProgress removeObjectForKey:key];
    }
    
    [self setNeedsDisplay];
}

@end

BNRLine.h

[code]#import <Foundation/Foundation.h>

@interface BNRLine : NSObject

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

@end
[/code]

BNRLine.m

[code]#import “BNRLine.h”

@implementation BNRLine

@synthesize begin, end;

@end[/code]


#4

I have found the following problems.

Problem 1: begin and end properties of BNRLine have incorrect types.

[BNRLine.h - wrong property type:

#import <Foundation/Foundation.h>

@interface BNRLine : NSObject

@property (nonatomic) CGPoint *begin;   // wrong type - pointer type is not appropriate
@property (nonatomic) CGPoint *end;     // ditto

@end

BNRLine.h - should be:

#import <Foundation/Foundation.h>

@interface BNRLine : NSObject

@property (nonatomic) CGPoint begin;
@property (nonatomic) CGPoint end;

@end

Therefore, you should remove all the ‘*’ and ‘&’ Xcode has asked you to put it.

Problem 2: BNRDrawView has the wrong type of initializer

BNRDrawView.m - inappropriate type of intializer for views created in a nib file

-(id)initWithFrame:(CGRect)r
{
   ...
}

BNRDrawView.m - should be:

-(id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder];
    ...
}

Problem 3: BNRDrawView.m is missing some code in - touchesMoved:withEvent:.

BNRDrawView.m - touchesMoved:withEvent: :

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    ...
    for (UITouch *t in touches) {
        ...
        // --- THIS WAS MISSING --- //
        // Update the line
        CGPoint loc = [t locationInView:self];
        [line setEnd:loc];
    }
    ...
}

Here is the revised code…

BNRLine.h:

//  BNRLine.h

#import <Foundation/Foundation.h>

@interface BNRLine : NSObject

@property (nonatomic) CGPoint begin;
@property (nonatomic) CGPoint end;

@end

BNRDrawView.m:

//  BNRDrawView.m

#import "BNRDrawView.h"
#import "BNRLine.h"

@interface BNRDrawView ()
{
    NSMutableDictionary *_linesInProgress;
    NSMutableArray *_finishedLines;
}
@end

@implementation BNRDrawView

/*
// initWithFrame can not be used with views created in a nib file
 
-(id)initWithFrame:(CGRect)r
{
    self = [super initWithFrame:r];
    
    if (self) {
        _linesInProgress = [[NSMutableDictionary alloc]init];
        _finishedLines = [[NSMutableArray alloc]init];
        
        [self setBackgroundColor:[UIColor grayColor]];
        [self setMultipleTouchEnabled:YES];
    }
    return self;
}
*/

-(id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder];
    
    if (self) {
        _linesInProgress = [[NSMutableDictionary alloc]init];
        _finishedLines = [[NSMutableArray alloc]init];
        
        [self setBackgroundColor:[UIColor grayColor]];
        [self setMultipleTouchEnabled:YES];
    }
    return self;
}

- (void)drawRect:(CGRect)rect
{
    NSLog (@"%s: %d %d", __func__, [_finishedLines count], [_linesInProgress count]);
    
    // Create a block that will configure and draw a BNRLine
    void (^strokeLine)(BNRLine *) = ^(BNRLine *line) {
        UIBezierPath *bp = [UIBezierPath bezierPath];
        [bp moveToPoint:[line begin]];
        [bp addLineToPoint:[line end]];
        [bp setLineWidth:10];
        [bp setLineCapStyle:kCGLineCapRound];
        
        [bp stroke];
    };
    
    // Draw finished lines in black
    [[UIColor blackColor] set];
    for (BNRLine *line in _finishedLines) {
        strokeLine(line);
    }
    [[UIColor redColor] set];
    for (NSValue *key in _linesInProgress) {
        strokeLine([_linesInProgress objectForKey:key]);
    }
}


- (void)touchesBegan:(NSSet *)touches
           withEvent:(UIEvent *)event
{
    // Lets put in a log statement to see the order of events
    NSLog(@"%@", NSStringFromSelector(_cmd));
    
    for (UITouch *t in touches) {
        CGPoint location = [t locationInView:self];
        BNRLine *line = [[BNRLine alloc]init];
        [line setBegin];
        [line setEnd];
        
        NSValue *key = [NSValue valueWithNonretainedObject:t];
        
        [_linesInProgress setObject:line forKey:key];
        
    }
    
    [self setNeedsDisplay];
}


- (void)touchesMoved:(NSSet *)touches
           withEvent:(UIEvent *)event
{
    
    // Put in a log statement to see the order of events
    NSLog(@"%@", NSStringFromSelector(_cmd));
    
    for (UITouch *t in touches) {
        NSValue *key = [NSValue valueWithNonretainedObject:t];
        BNRLine *line = [_linesInProgress objectForKey:key];
        
        // --- THIS WAS MISSING --- //
        // Update the line
        CGPoint loc = [t locationInView:self];
        [line setEnd:loc];
    }
    
    [self setNeedsDisplay];
}


- (void)touchesEnded:(NSSet *)touches
           withEvent:(UIEvent *)event
{
    NSLog(@"%@", NSStringFromSelector(_cmd));
    
    for (UITouch *t in touches) {
        NSValue *key = [NSValue valueWithNonretainedObject:t];
        BNRLine *line = [_linesInProgress objectForKey:key];
        
        
        [_finishedLines addObject:line];
        [_linesInProgress removeObjectForKey:key];
    }
    
    [self setNeedsDisplay];
}


- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    
    // Put in a log statement to see the order of events
    NSLog(@"%@", NSStringFromSelector(_cmd));
    
    for (UITouch *t in touches) {
        NSValue *key = [NSValue valueWithNonretainedObject:t];
        [_linesInProgress removeObjectForKey:key];
    }
    
    [self setNeedsDisplay];
}
@end

Please be extra careful, especially if you are a touch typer, when typing in the code from the book.


#5

You are the man, thanks a lot.

Everything worked but I didn’t use the -(id)initWithCoder:(NSCoder *)aDecoder method. It doesn’t mention to use this in the book, and I don’t have a nib file in this particular project. When I did put that code in my project, the screen went black and I wasn’t able to draw anything. Just a heads up.

Thanks again,