2

I have a very simple (well hopefully very simple) question. In Objective-C, how do you draw a line between two points and add it to a UIView? I have tried using a UIImageView and manipulating its Transform property, but that ends up turning the line into a square or a rectangle when using the following code:

[[self tline] setFrame:CGRectMake(start.x, start.y, width, 5)];
[[self tline] setTransform:CGAffineTransformMakeRotation(angle)];

I have two CGPoints, start and end, and I would like to draw a dynamic 5px line between the two points and add it to my subview.

BK:

The point start is the point where the user begins touching the screen, and the point end is the point where the user's finger is currently. Obviously this will move a lot during gameplay. I need to be able to move this line to connect these two points.

I am using the touchesBegan:, Moved:, and Ended: methods to create, move, and destroy the line.

CoreGraphics

I have the following code; how do I add this line to self.view?

CGContextRef c = UIGraphicsGetCurrentContext();
CGFloat color[4] = {1.0f, 1.0f, 1.0f, 0.6f};
CGContextSetStrokeColor(c, color);
CGContextBeginPath(c);
CGContextMoveToPoint(c, start.x, start.y);
CGContextAddLineToPoint(c, end.x, end.y);
CGContextSetLineWidth(c, 5);
CGContextSetLineCap(c, kCGLineCapRound);
CGContextStrokePath(c);

Custom UIView:

#import <UIKit/UIKit.h>

@interface DrawingView : UIView

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

- (void)drawRect:(CGRect)rect;

@end

#import "DrawingView.h"

@implementation DrawingView

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

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSaveGState(context);
    CGContextSetLineCap(context, kCGLineCapSquare);
    CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor); //change color here
    CGFloat lineWidth = 5.0; //change line width here
    CGContextSetLineWidth(context, lineWidth);
    CGPoint startPoint = [self start];
    CGPoint endPoint = [self end];
    CGContextMoveToPoint(context, startPoint.x + lineWidth/2, startPoint.y + lineWidth/2);
    CGContextAddLineToPoint(context, endPoint.x + lineWidth/2, endPoint.y + lineWidth/2);
    CGContextStrokePath(context);
    CGContextRestoreGState(context);

    NSLog(@"%f",_end.x);
}

- (void)setEnd:(CGPoint)end
{
    _end = end;
    [self setNeedsDisplay];
}

@end

drawRect: is only called when I initialize the view...

Draw method in UIViewController:

- (void)drawTLine:(CGPoint)start withEndPoint:(CGPoint)end
{   
    [[self dview] setStart:start];
    [[self dview] setEnd:end];
    [[self dview] drawRect:[self dview].frame];
}

This is how I add the drawing view:

DrawingView* dview = [[DrawingView alloc] initWithFrame:self.view.frame];
[dview setBackgroundColor:[UIColor clearColor]];
[self.view addSubview:dview];
Liftoff
  • 24,717
  • 13
  • 66
  • 119
  • 1
    Have you considered Core Graphics, using the `draw:` method of a UIView? – anon_dev1234 Mar 27 '13 at 20:39
  • If I use the draw method, how will I keep track of the line? I need to be able to move it, resize it, and destroy it, as necessary. – Liftoff Mar 27 '13 at 20:41
  • 1
    Well in your moved event, you can perhaps update a property/variable to keep track of current position. The `draw:` method is called every update, so you would just draw a point between start and current, and erase any old lines. Clearing is pretty simple as outline here -> http://stackoverflow.com/a/7907669/1415949 – anon_dev1234 Mar 27 '13 at 20:43
  • Gabriele's answer should work well as long as you are drawing straight lines – anon_dev1234 Mar 27 '13 at 21:06
  • Two things; how do I implement this to the view of a UIViewController, and how do I call this method? I need to give it two CGPoints to draw the line between. – Liftoff Mar 27 '13 at 21:08
  • Create a custom view - set that as your view controller's view in `viewDidLoad:`. Then in your custom view, implement the `drawRect:` method - once it is implemented, it is automatically called every run through the event loop. I would suggest reading up on `UIView` and `CoreGraphics` through the Apple docs. – anon_dev1234 Mar 27 '13 at 21:17
  • See http://stackoverflow.com/questions/5847876/whats-the-best-approach-to-draw-lines-between-views/5848118#5848118 – Nikolai Ruhe Mar 27 '13 at 21:28
  • @NikolaiRuhe That worked perfectly! All I had to do was disable implicit animation. Great explanation! Thank you! Move it to an answer and I'll give you the answer point! – Liftoff Mar 27 '13 at 21:45
  • That's ok. You can upvote the linked answer if you like. – Nikolai Ruhe Mar 28 '13 at 06:54

2 Answers2

3

Subclass UIView and perform custom drawing using the drawRect: method

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSaveGState(context);
    CGContextSetLineCap(context, kCGLineCapSquare);
    CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor); //change color here
    CGFloat lineWidth = 1.0; //change line width here
    CGContextSetLineWidth(context, lineWidth); 
    CGPoint startPoint = rect.origin; //change start point here
    CGPoint endPoint = {.x = CGRectGetMaxX(rect), .y = startPoint.y} //change end point here
    CGContextMoveToPoint(context, startPoint.x + lineWidth/2, startPoint.y + lineWidth/2);
    CGContextAddLineToPoint(context, endPoint.x + lineWidth/2, endPoint.y + lineWidth/2);
    CGContextStrokePath(context);
    CGContextRestoreGState(context);        
}

This will draw a black 1px line at the top of your UIView.

If you need to update the line you can just use some properties like

@property (nonatomic) CGPoint startPoint;

and provide a custom implementation for the setter like

- (void)setStartPoint:(CGPoint)point {
      _point = point;
      [self setNeedsDisplay]; // this will cause drawRect: to be called again
}

Do that for every property that you wish to control and make your drawRect: use such properties for drawing.

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
  • There's no need to save and restore the context — UIView will have set up a context already: [*...UIKit creates and configures a graphics context for drawing and adjusts the transform of that context so that its origin matches the origin of your view’s bounds rectangle.*](http://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/instm/UIView/drawRect:) – Caleb Mar 27 '13 at 20:55
  • I know, but it doesn't hurt and it makes everything neater if you are performing other custom drawing after that ;) – Gabriele Petronella Mar 27 '13 at 21:06
  • Okay I added this as a custom UIView and implemented the method to set the end point (instead of the start point), but drawRect: is not being called after the view is added. – Liftoff Mar 27 '13 at 21:23
  • How are you adding the view? – Gabriele Petronella Mar 27 '13 at 21:25
  • You are doing a few things wrong. You don't need to declare `- (void)drawRect:(CGRect)rect;` on top since it's an existing method of `UIView`. Moreover you don't need to manually call `drawRect:`. It gets called whenever the view's frame changes, e.g. when initializing it with `initWithFrame:` – Gabriele Petronella Mar 27 '13 at 21:32
0

you can create and UIBezierPath like this

UIBezierPath path = [[UIBezierPath alloc] init]
[path moveToPoint: CGPoint(x,y)] // X and Y, start point
[path addLineToPoint:CGPoint(x2,y2) // X2 and Y2, end point

if you want to create a shape you can put more points with addLineToPoint: method and finish use closePath method

I hope help you

kakashy
  • 714
  • 9
  • 24