Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Draw a separator line in conjunction with Auto Layout

I'm reimplementig some kind of UISplitViewController. Now I need to draw a separator line between the master and the detail view. Now I have some questions for this:

  • Should the separator line be on the view controller itself or should it be a separate view?
  • What about Auto Layout? Setting a frame is not allowed.

I saw solutions for CALayer/CAShapeLayer, drawRect (CoreGraphics) or using the background color of an UIView/UILabel. The last one should cost too much performance according to the folks there.

On the one side it is comfortable to draw the line in the UITableViewController itself. Or should a separate UIView be created? If I embed a separate UIView there will be much more constraints and it will complicate things (would have to set separate widths) ... Also it should adapt to orientation changes (e.g. the size of the UITableViewController changes -> the separation line should also resize).

How can I add such a dividing rule? Such a dividing rule can be seen here: Separator line divides master and detail view controller

like image 451
testing Avatar asked Oct 23 '14 08:10

testing


3 Answers

If you need to add a true one pixel line, don't fool with an image. It's almost impossible. Just use this:

@interface UILine : UIView
@end

@implementation UILine

- (void)awakeFromNib {

    CGFloat sortaPixel = 1 / [UIScreen mainScreen].scale;

    // recall, the following...
    // CGFloat sortaPixel = 1 / self.contentScaleFactor;
    // ...does NOT work when loading from storyboard!

    UIView *line = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, sortaPixel)];

    line.userInteractionEnabled = NO;
    line.backgroundColor = self.backgroundColor;

    line.autoresizingMask = UIViewAutoresizingFlexibleWidth;

    [self addSubview:line];

    self.backgroundColor = [UIColor clearColor];
    self.userInteractionEnabled = NO;
}

@end

How to use:

Actually in storyboard, simply make a UIView that is in the exact place, and exact width, you want. (Feel free to use constraints/autolayout as normal.)

Make the view say five pixels high, simply so you can see it clearly, while working.

Make the top of the UIView exactly where you want the single-pixel line. Make the UIView the desired color of the line.

Change the class to UILine. At run time, it will draw a perfect single-pixel line in the exact location on all devices.

(For a vertical line class, simply modify the CGRectMake.)

Hope it helps!

like image 118
Fattie Avatar answered Oct 17 '22 10:10

Fattie


I took @joe-blow's excellent answer a step further and created a view that is not only rendered in IB but also whose line width and line color can be changed via the inspector in IB. Simply add a UIView to your storyboard, size appropriately, and change the class to LineView.

import UIKit

@IBDesignable
class LineView: UIView {

  @IBInspectable var lineWidth: CGFloat = 1.0
  @IBInspectable var lineColor: UIColor? {
    didSet {
      lineCGColor = lineColor?.CGColor
    }
  }
  var lineCGColor: CGColorRef?

    override func drawRect(rect: CGRect) {
      // Draw a line from the left to the right at the midpoint of the view's rect height.
      let midpoint = self.bounds.size.height / 2.0
      let context = UIGraphicsGetCurrentContext()
      CGContextSetLineWidth(context, lineWidth)
      if let lineCGColor = self.lineCGColor {
        CGContextSetStrokeColorWithColor(context, lineCGColor)
      }
      else {
        CGContextSetStrokeColorWithColor(context, UIColor.blackColor().CGColor)
      }
      CGContextMoveToPoint(context, 0.0, midpoint)
      CGContextAddLineToPoint(context, self.bounds.size.width, midpoint)
      CGContextStrokePath(context)
    }
}

Inspector for LineView

LineView rendered in storybard

like image 28
mharper Avatar answered Oct 17 '22 12:10

mharper


Updating the @mharper answer to Swift 3.x

import UIKit

@IBDesignable
class LineView: UIView {

    @IBInspectable var lineWidth: CGFloat = 1.0
    @IBInspectable var lineColor: UIColor? {
        didSet {
            lineCGColor = lineColor?.cgColor
        }
    }
    var lineCGColor: CGColor?

    override func draw(_ rect: CGRect) {
        // Draw a line from the left to the right at the midpoint of the view's rect height.
        let midpoint = self.bounds.size.height / 2.0
        if let context = UIGraphicsGetCurrentContext() {
            context.setLineWidth(lineWidth)
            if let lineCGColor = self.lineCGColor {
                context.setStrokeColor(lineCGColor)
            }
            else {
                context.setStrokeColor(UIColor.black.cgColor)
            }
            context.move(to: CGPoint(x: 0.0, y: midpoint))
            context.addLine(to: CGPoint(x: self.bounds.size.width, y: midpoint))
            context.strokePath()
        }
    }
}
like image 31
YMonnier Avatar answered Oct 17 '22 12:10

YMonnier