90

I need to draw a horizontal line in a UIView. What is the easiest way to do it. For example, I want to draw a black horizontal line at y-coord=200.

I am NOT using Interface Builder.

AVerguno
  • 1,277
  • 11
  • 27
John Smith
  • 12,491
  • 18
  • 65
  • 111
  • 1
    Here's the complete & easy solution to correctly having a single-pixel line, in all iOS, and making it easy in storyboard: http://stackoverflow.com/a/26525466/294884 – Fattie Oct 23 '14 at 12:05

9 Answers9

314

Maybe this is a bit late, but I want to add that there is a better way. Using UIView is simple, but relatively slow. This method overrides how the view draws itself and is faster:

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);

    // Draw them with a 2.0 stroke width so they are a bit more visible.
    CGContextSetLineWidth(context, 2.0f);

    CGContextMoveToPoint(context, 0.0f, 0.0f); //start at this point

    CGContextAddLineToPoint(context, 20.0f, 20.0f); //draw to this point

    // and now draw the Path!
    CGContextStrokePath(context);
}
John Riselvato
  • 12,854
  • 5
  • 62
  • 89
b123400
  • 6,138
  • 4
  • 27
  • 27
  • If this code is used in a UIView are the coordinates in this sample relative to the bounds of the view or the entire screen? – ChrisP Sep 16 '11 at 17:53
  • Does one not need to call `CGContextBeginPath(context);` before `CGContextMoveToPoint(...);`? – i_am_jorf Nov 09 '11 at 18:38
  • 38
    Using a view to do it isn't too horrible. It makes things easier, for example, when doing implicit animations during orientation changes. If it's just one, another UIView isn't all that expensive and the code is much simpler. – i_am_jorf Feb 02 '12 at 21:54
  • 1
    In addition, you can get bounds and midpoint with these codes: CGPoint midPoint; midPoint.x = self.bounds.size.width / 2; midPoint.y = self.bounds.size.height / 2; – coolcool1994 Mar 03 '13 at 04:00
  • @jeffamaphone It can also be useful when using autolayout, if the most natural way to define the length or positioning of the line happens to be by constraints. – Mark Amery Sep 26 '13 at 12:58
  • I did this but when I resize the view the line gets distorted. – jspooner Jan 12 '14 at 04:11
  • 1
    Right, the comment that it is "slow" is not sensible. There are huge numbers of simple UIViews on screen at any time. Note that drawing ONE (!) text character, with all the processing that involves, swamps drawing a filled UIView. – Fattie Oct 23 '14 at 12:02
  • As of Apple Docs, there is not need to call `[super drawRect:rect]`. Quote: "If you subclass UIView directly, your implementation of this method does not need to call super. However, if you are subclassing a different view class, you should call super at some point in your implementation." – Yevhen Dubinin Nov 11 '14 at 23:19
122

The easiest way in your case (horizontal line) is to add a subview with black background color and frame [0, 200, 320, 1].

Code sample (I hope there are no errors - I wrote it without Xcode):

UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(0, 200, self.view.bounds.size.width, 1)];
lineView.backgroundColor = [UIColor blackColor];
[self.view addSubview:lineView];
[lineView release];
// You might also keep a reference to this view 
// if you are about to change its coordinates.
// Just create a member and a property for this...

Another way is to create a class that will draw a line in its drawRect method (you can see my code sample for this here).

Community
  • 1
  • 1
Michael Kessler
  • 14,245
  • 13
  • 50
  • 64
37

Swift 3 and Swift 4

This is how you can draw a gray line at the end of your view (same idea as b123400's answer)

class CustomView: UIView {

    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        if let context = UIGraphicsGetCurrentContext() {
            context.setStrokeColor(UIColor.gray.cgColor)
            context.setLineWidth(1)
            context.move(to: CGPoint(x: 0, y: bounds.height))
            context.addLine(to: CGPoint(x: bounds.width, y: bounds.height))
            context.strokePath()
        }
    }
}
Community
  • 1
  • 1
Guy Daher
  • 5,526
  • 5
  • 42
  • 67
15

Just add a Label without text and with background color. Set the Coordinates of your choice and also height and width. You can do it manually or with Interface Builder.

Phanindra
  • 195
  • 1
  • 4
13

You can user UIBezierPath Class for this:

And can draw as many lines as you want:

I have subclassed UIView :

    @interface MyLineDrawingView()
    {
       NSMutableArray *pathArray;
       NSMutableDictionary *dict_path;
       CGPoint startPoint, endPoint;
    }

       @property (nonatomic,retain)   UIBezierPath *myPath;
    @end

And initialized the pathArray and dictPAth objects which will be used for line drawing. I am writing the main portion of the code from my own project:

- (void)drawRect:(CGRect)rect
{

    for(NSDictionary *_pathDict in pathArray)
    {
        [((UIColor *)[_pathDict valueForKey:@"color"]) setStroke]; // this method will choose the color from the receiver color object (in this case this object is :strokeColor)
        [[_pathDict valueForKey:@"path"] strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];
    }

    [[dict_path objectForKey:@"color"] setStroke]; // this method will choose the color from the receiver color object (in this case this object is :strokeColor)
    [[dict_path objectForKey:@"path"] strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];

}

touchesBegin method :

UITouch *touch = [touches anyObject];
startPoint = [touch locationInView:self];
myPath=[[UIBezierPath alloc]init];
myPath.lineWidth = currentSliderValue*2;
dict_path = [[NSMutableDictionary alloc] init];

touchesMoved Method:

UITouch *touch = [touches anyObject];
endPoint = [touch locationInView:self];

 [myPath removeAllPoints];
        [dict_path removeAllObjects];// remove prev object in dict (this dict is used for current drawing, All past drawings are managed by pathArry)

    // actual drawing
    [myPath moveToPoint:startPoint];
    [myPath addLineToPoint:endPoint];

    [dict_path setValue:myPath forKey:@"path"];
    [dict_path setValue:strokeColor forKey:@"color"];

    //                NSDictionary *tempDict = [NSDictionary dictionaryWithDictionary:dict_path];
    //                [pathArray addObject:tempDict];
    //                [dict_path removeAllObjects];
    [self setNeedsDisplay];

touchesEnded Method:

        NSDictionary *tempDict = [NSDictionary dictionaryWithDictionary:dict_path];
        [pathArray addObject:tempDict];
        [dict_path removeAllObjects];
        [self setNeedsDisplay];
YogiAR
  • 2,207
  • 23
  • 44
12

One other (and an even shorter) possibility. If you're inside drawRect, something like the following:

[[UIColor blackColor] setFill];
UIRectFill((CGRect){0,200,rect.size.width,1});
hkatz
  • 951
  • 11
  • 21
2

Swift 3, 4, 5

This is the simplest of all I could find...

let lineView = UIView(frame: CGRect(x: 4, y: 50, width: self.view.frame.width, height: 1))
lineView.backgroundColor = .lightGray
self.view.addSubview(lineView)
Sujay U N
  • 4,974
  • 11
  • 52
  • 88
1

Based on Guy Daher's answer.

I try to avoid using ? because it can cause an application crash if the GetCurrentContext() returns nil.

I would do nil check if statement:

class CustomView: UIView 
{    
    override func draw(_ rect: CGRect) 
    {
        super.draw(rect)
        if let context = UIGraphicsGetCurrentContext()
        {
            context.setStrokeColor(UIColor.gray.cgColor)
            context.setLineWidth(1)
            context.move(to: CGPoint(x: 0, y: bounds.height))
            context.addLine(to: CGPoint(x: bounds.width, y: bounds.height))
            context.strokePath()
        }
    }
}
-1

Add label without text and with background color corresponding frame size(ex:height=1). Do it through code or in interface builder.

Reddy
  • 19
  • 6