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

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


I consider subclassing UIView and overriding drawRect overkill here. Why not add an extension on UIView and add border subviews?

@discardableResultfunc addBorders(edges: UIRectEdge,                color: UIColor,                inset: CGFloat = 0.0,                thickness: CGFloat = 1.0) -> [UIView] {    var borders = [UIView]()    @discardableResult    func addBorder(formats: String...) -> UIView {        let border = UIView(frame: .zero)        border.backgroundColor = color        border.translatesAutoresizingMaskIntoConstraints = false        addSubview(border)        addConstraints(formats.flatMap {            NSLayoutConstraint.constraints(withVisualFormat: $0,                                           options: [],                                           metrics: ["inset": inset, "thickness": thickness],                                           views: ["border": border]) })        borders.append(border)        return border    }    if edges.contains(.top) || edges.contains(.all) {        addBorder(formats: "V:|-0-[border(==thickness)]", "H:|-inset-[border]-inset-|")    }    if edges.contains(.bottom) || edges.contains(.all) {        addBorder(formats: "V:[border(==thickness)]-0-|", "H:|-inset-[border]-inset-|")    }    if edges.contains(.left) || edges.contains(.all) {        addBorder(formats: "V:|-inset-[border]-inset-|", "H:|-0-[border(==thickness)]")    }    if edges.contains(.right) || edges.contains(.all) {        addBorder(formats: "V:|-inset-[border]-inset-|", "H:[border(==thickness)]-0-|")    }    return borders}    // Usage:             view.addBorder(edges: [.all]) // All with default arguments     view.addBorder(edges: [.top], color: .green) // Just Top, green, default thickness    view.addBorder(edges: [.left, .right, .bottom], color: .red, thickness: 3) // All except Top, red, thickness 3

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. Pass in other arguments to your methods to define other colours and widths. Many options.


Added capability for rounded corners to Adam Waite's original post, and the multiple edits

Important!: Don't forgot to add 'label.layoutIfNeeded()' right before calling 'addborder' as previously commented

Note: I've only tested this on UILabels.

extension CALayer {        enum BorderSide {        case top        case right        case bottom        case left        case notRight        case notLeft        case topAndBottom        case all    }        enum Corner {        case topLeft        case topRight        case bottomLeft        case bottomRight    }        func addBorder(side: BorderSide, thickness: CGFloat, color: CGColor, maskedCorners: CACornerMask? = nil) {        var topWidth = frame.size.width; var bottomWidth = topWidth        var leftHeight = frame.size.height; var rightHeight = leftHeight                var topXOffset: CGFloat = 0; var bottomXOffset: CGFloat = 0        var leftYOffset: CGFloat = 0; var rightYOffset: CGFloat = 0                // Draw the corners and set side offsets        switch maskedCorners {        case [.layerMinXMinYCorner, .layerMaxXMinYCorner]: // Top only            addCorner(.topLeft, thickness: thickness, color: color)            addCorner(.topRight, thickness: thickness, color: color)            topWidth -= cornerRadius*2            leftHeight -= cornerRadius; rightHeight -= cornerRadius            topXOffset = cornerRadius; leftYOffset = cornerRadius; rightYOffset = cornerRadius                    case [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]: // Bottom only            addCorner(.bottomLeft, thickness: thickness, color: color)            addCorner(.bottomRight, thickness: thickness, color: color)            bottomWidth -= cornerRadius*2            leftHeight -= cornerRadius; rightHeight -= cornerRadius            bottomXOffset = cornerRadius                    case [.layerMinXMinYCorner, .layerMinXMaxYCorner]: // Left only            addCorner(.topLeft, thickness: thickness, color: color)            addCorner(.bottomLeft, thickness: thickness, color: color)            topWidth -= cornerRadius; bottomWidth -= cornerRadius            leftHeight -= cornerRadius*2            leftYOffset = cornerRadius; topXOffset = cornerRadius; bottomXOffset = cornerRadius;                    case [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]: // Right only            addCorner(.topRight, thickness: thickness, color: color)            addCorner(.bottomRight, thickness: thickness, color: color)            topWidth -= cornerRadius; bottomWidth -= cornerRadius            rightHeight -= cornerRadius*2            rightYOffset = cornerRadius                    case [.layerMaxXMinYCorner, .layerMaxXMaxYCorner,  // All              .layerMinXMaxYCorner, .layerMinXMinYCorner]:            addCorner(.topLeft, thickness: thickness, color: color)            addCorner(.topRight, thickness: thickness, color: color)            addCorner(.bottomLeft, thickness: thickness, color: color)            addCorner(.bottomRight, thickness: thickness, color: color)            topWidth -= cornerRadius*2; bottomWidth -= cornerRadius*2            topXOffset = cornerRadius; bottomXOffset = cornerRadius            leftHeight -= cornerRadius*2; rightHeight -= cornerRadius*2            leftYOffset = cornerRadius; rightYOffset = cornerRadius                    default: break        }                // Draw the sides        switch side {        case .top:            addLine(x: topXOffset, y: 0, width: topWidth, height: thickness, color: color)                    case .right:            addLine(x: frame.size.width - thickness, y: rightYOffset, width: thickness, height: rightHeight, color: color)                    case .bottom:            addLine(x: bottomXOffset, y: frame.size.height - thickness, width: bottomWidth, height: thickness, color: color)                    case .left:            addLine(x: 0, y: leftYOffset, width: thickness, height: leftHeight, color: color)        // Multiple Sides        case .notRight:            addLine(x: topXOffset, y: 0, width: topWidth, height: thickness, color: color)            addLine(x: 0, y: leftYOffset, width: thickness, height: leftHeight, color: color)            addLine(x: bottomXOffset, y: frame.size.height - thickness, width: bottomWidth, height: thickness, color: color)        case .notLeft:            addLine(x: topXOffset, y: 0, width: topWidth, height: thickness, color: color)            addLine(x: frame.size.width - thickness, y: rightYOffset, width: thickness, height: rightHeight, color: color)            addLine(x: bottomXOffset, y: frame.size.height - thickness, width: bottomWidth, height: thickness, color: color)        case .topAndBottom:            addLine(x: topXOffset, y: 0, width: topWidth, height: thickness, color: color)            addLine(x: bottomXOffset, y: frame.size.height - thickness, width: bottomWidth, height: thickness, color: color)        case .all:            addLine(x: topXOffset, y: 0, width: topWidth, height: thickness, color: color)            addLine(x: frame.size.width - thickness, y: rightYOffset, width: thickness, height: rightHeight, color: color)            addLine(x: bottomXOffset, y: frame.size.height - thickness, width: bottomWidth, height: thickness, color: color)            addLine(x: 0, y: leftYOffset, width: thickness, height: leftHeight, color: color)        }    }        private func addLine(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat, color: CGColor) {        let border = CALayer()        border.frame = CGRect(x: x, y: y, width: width, height: height)        border.backgroundColor = color        addSublayer(border)    }        private func addCorner(_ corner: Corner, thickness: CGFloat, color: CGColor) {        // Set default to top left        let width = frame.size.width; let height = frame.size.height        var x = cornerRadius        var y = cornerRadius        var startAngle: CGFloat = .pi; var endAngle: CGFloat = .pi*3/2                switch corner {        case .bottomLeft:            y = height - cornerRadius             startAngle = .pi/2; endAngle = .pi                    case .bottomRight:            x = width - cornerRadius            y = height - cornerRadius            startAngle = 0; endAngle = .pi/2                    case .topRight:            x = width - cornerRadius            startAngle = .pi*3/2; endAngle = 0                    default: break        }                let cornerPath = UIBezierPath(arcCenter: CGPoint(x: x, y: y),                                      radius: cornerRadius - thickness,                                      startAngle: startAngle,                                      endAngle: endAngle,                                      clockwise: true)        let cornerShape = CAShapeLayer()        cornerShape.path = cornerPath.cgPath        cornerShape.lineWidth = thickness        cornerShape.strokeColor = color        cornerShape.fillColor = nil        addSublayer(cornerShape)    }}


The best way for me is a category on UIView, but adding views instead of CALayers, so we can take advantage of AutoresizingMasks to make sure borders resize along with the superview.

Objective C

- (void)addTopBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth {    UIView *border = [UIView new];    border.backgroundColor = color;    [border setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin];    border.frame = CGRectMake(0, 0, self.frame.size.width, borderWidth);    [self addSubview:border];}- (void)addBottomBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth {    UIView *border = [UIView new];    border.backgroundColor = color;    [border setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin];    border.frame = CGRectMake(0, self.frame.size.height - borderWidth, self.frame.size.width, borderWidth);    [self addSubview:border];}- (void)addLeftBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth {    UIView *border = [UIView new];    border.backgroundColor = color;    border.frame = CGRectMake(0, 0, borderWidth, self.frame.size.height);    [border setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleRightMargin];    [self addSubview:border];}- (void)addRightBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth {    UIView *border = [UIView new];    border.backgroundColor = color;    [border setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin];    border.frame = CGRectMake(self.frame.size.width - borderWidth, 0, borderWidth, self.frame.size.height);    [self addSubview:border];}

Swift 5

func addTopBorder(with color: UIColor?, andWidth borderWidth: CGFloat) {    let border = UIView()    border.backgroundColor = color    border.autoresizingMask = [.flexibleWidth, .flexibleBottomMargin]    border.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: borderWidth)    addSubview(border)}func addBottomBorder(with color: UIColor?, andWidth borderWidth: CGFloat) {    let border = UIView()    border.backgroundColor = color    border.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]    border.frame = CGRect(x: 0, y: frame.size.height - borderWidth, width: frame.size.width, height: borderWidth)    addSubview(border)}func addLeftBorder(with color: UIColor?, andWidth borderWidth: CGFloat) {    let border = UIView()    border.backgroundColor = color    border.frame = CGRect(x: 0, y: 0, width: borderWidth, height: frame.size.height)    border.autoresizingMask = [.flexibleHeight, .flexibleRightMargin]    addSubview(border)}func addRightBorder(with color: UIColor?, andWidth borderWidth: CGFloat) {    let border = UIView()    border.backgroundColor = color    border.autoresizingMask = [.flexibleHeight, .flexibleLeftMargin]    border.frame = CGRect(x: frame.size.width - borderWidth, y: 0, width: borderWidth, height: frame.size.height)    addSubview(border)}