manonthemoon manonthemoon - 2 months ago 10
Objective-C Question

How to add a border just on the top side of a UIView

My question is on the title.

I don't know how to add a border in a specific side, top or bottom, any side...

layer.border
draws the border for the whole view...

Help please :S

Thanks

Answer

I consider subclassing UIView and overriding drawRect overkill here. Why not add a category on UIView and use CALayers

- (void)prefix_addUpperBorder
{
  CALayer *upperBorder = [CALayer layer];
  upperBorder.backgroundColor = [[UIColor greenColor] CGColor];
  upperBorder.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), 1.0f);
  [self.layer addSublayer:upperBorder];
}

With this code you're not tied to your subclass too, you can apply it to anything and everything that inherits from UIView - reusable in your project, and any others. You could even add a typedef and make the method switch to determine the side to render the border on. Pass in other arguments to your methods to define other colours and widths. Many options.

Worth mentioning to use CALayer will require you to #import <QuartzCore/QuartzCore.h>. I usually do that in some global place like the precompiled header.


Edit 05/05/15

I came to need the functionality I described in my original answer - "You could even add a typedef and make the method switch to determine the side to render the border on".

Thought I may as well add it to this answer:

- (CALayer *)prefix_addUpperBorder:(UIRectEdge)edge color:(UIColor *)color thickness:(CGFloat)thickness
{
    CALayer *border = [CALayer layer];

    switch (edge) {
        case UIRectEdgeTop:
            border.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), thickness);
            break;
        case UIRectEdgeBottom:
            border.frame = CGRectMake(0, CGRectGetHeight(self.frame) - thickness, CGRectGetWidth(self.frame), thickness);
            break;
        case UIRectEdgeLeft:
            border.frame = CGRectMake(0, 0, thickness, CGRectGetHeight(self.frame));
            break;
        case UIRectEdgeRight:
            border.frame = CGRectMake(CGRectGetWidth(self.frame) - thickness, 0, thickness, CGRectGetHeight(self.frame));
            break;
        default:
            break;
    }

    border.backgroundColor = color.CGColor;

    [self.layer addSublayer:border];

    return border;
}

Edit 19/10/15

People have been asking for an AutoLayout type of solution. Here you go (moved to Swift now too):

func addBorder(edges edges: UIRectEdge, colour: UIColor = UIColor.whiteColor(), thickness: CGFloat = 1) -> [UIView] {

    var borders = [UIView]()

    func border() -> UIView {
        let border = UIView(frame: CGRectZero)
        border.backgroundColor = colour
        border.translatesAutoresizingMaskIntoConstraints = false
        return border
    }

    if edges.contains(.Top) || edges.contains(.All) {
        let top = border()
        addSubview(top)
        addConstraints(
            NSLayoutConstraint.constraintsWithVisualFormat("V:|-(0)-[top(==thickness)]",
                options: [],
                metrics: ["thickness": thickness],
                views: ["top": top]))
        addConstraints(
            NSLayoutConstraint.constraintsWithVisualFormat("H:|-(0)-[top]-(0)-|",
                options: [],
                metrics: nil,
                views: ["top": top]))
        borders.append(top)
    }

    if edges.contains(.Left) || edges.contains(.All) {
        let left = border()
        addSubview(left)
        addConstraints(
            NSLayoutConstraint.constraintsWithVisualFormat("H:|-(0)-[left(==thickness)]",
                options: [],
                metrics: ["thickness": thickness],
                views: ["left": left]))
        addConstraints(
            NSLayoutConstraint.constraintsWithVisualFormat("V:|-(0)-[left]-(0)-|",
                options: [],
                metrics: nil,
                views: ["left": left]))
        borders.append(left)
    }

    if edges.contains(.Right) || edges.contains(.All) {
        let right = border()
        addSubview(right)
        addConstraints(
            NSLayoutConstraint.constraintsWithVisualFormat("H:[right(==thickness)]-(0)-|",
                options: [],
                metrics: ["thickness": thickness],
                views: ["right": right]))
        addConstraints(
            NSLayoutConstraint.constraintsWithVisualFormat("V:|-(0)-[right]-(0)-|",
                options: [],
                metrics: nil,
                views: ["right": right]))
        borders.append(right)
    }

    if edges.contains(.Bottom) || edges.contains(.All) {
        let bottom = border()
        addSubview(bottom)
        addConstraints(
            NSLayoutConstraint.constraintsWithVisualFormat("V:[bottom(==thickness)]-(0)-|",
                options: [],
                metrics: ["thickness": thickness],
                views: ["bottom": bottom]))
        addConstraints(
            NSLayoutConstraint.constraintsWithVisualFormat("H:|-(0)-[bottom]-(0)-|",
                options: [],
                metrics: nil,
                views: ["bottom": bottom]))
        borders.append(bottom)
    }

    return borders
}

// Usage:         
container.addBorder(edges: [.All]) // All with default arguments 
container.addBorder(edges: [.Top], colour: UIColor.greenColor()) // Just Top, green, default thickness
container.addBorder(edges: [.Left, .Right, .Bottom], colour: UIColor.redColor(), thickness: 3) // All except Top, red, thickness 3
Comments