UIView borde inferior?

Para un UIScrollView *toScrollView (que es el ancho de la pantalla), quiero agregar un borde inferior gris (exactamente como el del campo de la vista de redacción de la aplicación de mensajes nativa del iPhone).

Para lograr esto, seguí Cocoa Touch: ¿Cómo cambiar el color y el grosor del borde de UIView? y simplemente cubrió el borde superior con la UINavigationBar personalizada e hizo la toScrollView x de toScrollView -1 y el ancho 322 para que los bordes izquierdo y derecho quedaran fuera de la pantalla.

Esto se ve bien, pero es una especie de truco, y me preguntaba si hay una mejor manera de hacerlo.

 - (void)viewDidLoad { [super viewDidLoad]; // Add UINavigationBar *navigationBar at top. self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelAction)]; UINavigationBar *navigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 44.0f)]; navigationBar.items = [NSArray arrayWithObject:self.navigationItem]; // Add UIScrollView *toScrollView below navigationBar. UIScrollView *toScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(-1.0f, 43.0f, 322.0f, 45.0f)]; toScrollView.backgroundColor = [UIColor whiteColor]; toScrollView.layer.borderColor = [UIColor colorWithWhite:0.8f alpha:1.0f].CGColor; toScrollView.layer.borderWidth = 1.0f; [self.view addSubview:toScrollView]; [self.view addSubview:navigationBar]; // covers top of toScrollView } 

En lugar de usar un UIView , como lo sugiere @ ImreKelényi, puede usar un CALayer :

 // Add a bottomBorder. CALayer *bottomBorder = [CALayer layer]; bottomBorder.frame = CGRectMake(0.0f, 43.0f, toScrollView.frame.size.width, 1.0f); bottomBorder.backgroundColor = [UIColor colorWithWhite:0.8f alpha:1.0f].CGColor; [toScrollView.layer addSublayer:bottomBorder]; 

Implementado en una categoría:

UIButton + Border.h:

 @interface UIButton (Border) - (void)addBottomBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth; - (void)addLeftBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth; - (void)addRightBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth; - (void)addTopBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth; @end 

UIButton + Border.m:

 @implementation UIButton (Border) - (void)addTopBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth { CALayer *border = [CALayer layer]; border.backgroundColor = color.CGColor; border.frame = CGRectMake(0, 0, self.frame.size.width, borderWidth); [self.layer addSublayer:border]; } - (void)addBottomBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth { CALayer *border = [CALayer layer]; border.backgroundColor = color.CGColor; border.frame = CGRectMake(0, self.frame.size.height - borderWidth, self.frame.size.width, borderWidth); [self.layer addSublayer:border]; } - (void)addLeftBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth { CALayer *border = [CALayer layer]; border.backgroundColor = color.CGColor; border.frame = CGRectMake(0, 0, borderWidth, self.frame.size.height); [self.layer addSublayer:border]; } - (void)addRightBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth { CALayer *border = [CALayer layer]; border.backgroundColor = color.CGColor; border.frame = CGRectMake(self.frame.size.width - borderWidth, 0, borderWidth, self.frame.size.height); [self.layer addSublayer:border]; } 

¡Espero eso ayude!

Aquí hay una extensión Swift más general para crear bordes para cualquier subclase UIView :

 import UIKit extension UIView { func addTopBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.CGColor border.frame = CGRectMake(0, 0, self.frame.size.width, width) self.layer.addSublayer(border) } func addRightBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.CGColor border.frame = CGRectMake(self.frame.size.width - width, 0, width, self.frame.size.height) self.layer.addSublayer(border) } func addBottomBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.CGColor border.frame = CGRectMake(0, self.frame.size.height - width, self.frame.size.width, width) self.layer.addSublayer(border) } func addLeftBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.CGColor border.frame = CGRectMake(0, 0, width, self.frame.size.height) self.layer.addSublayer(border) } } 

Swift 3

 extension UIView { func addTopBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.cgColor border.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: width) self.layer.addSublayer(border) } func addRightBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.cgColor border.frame = CGRect(x: self.frame.size.width - width, y: 0, width: width, height: self.frame.size.height) self.layer.addSublayer(border) } func addBottomBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.cgColor border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: width) self.layer.addSublayer(border) } func addLeftBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.cgColor border.frame = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height) self.layer.addSublayer(border) } } 

Puede agregar una vista independiente UIView con 1 punto de altura y color de fondo gris a self.view y self.view justo debajo toScrollView .

EDITAR: a menos que tenga una buena razón (desea usar algunos servicios de UIView que no son ofrecidos por CALayer), debe usar CALayer como sugiere @MattDiPasquale . UIView tiene una sobrecarga mayor, lo que podría no ser un problema en la mayoría de los casos, pero aún así, la otra solución es más elegante.

Si necesita una solución realmente adaptable (para todos los tamaños de pantalla), entonces este es:

 /** * Extends UIView with shortcut methods * * @author Alexander Volkov * @version 1.0 */ extension UIView { /** Adds bottom border to the view with given side margins - parameter color: the border color - parameter margins: the left and right margin */ func addBottomBorder(color: UIColor = UIColor.lightBorderColor(), margins: CGFloat = 0) { let border = UIView() border.backgroundColor = color border.translatesAutoresizingMaskIntoConstraints = false self.addSubview(border) border.addConstraint(NSLayoutConstraint(item: border, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.Height, multiplier: 1, constant: 1)) self.addConstraint(NSLayoutConstraint(item: border, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 0)) self.addConstraint(NSLayoutConstraint(item: border, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: margins)) self.addConstraint(NSLayoutConstraint(item: border, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Trailing, multiplier: 1, constant: margins)) } } 

También hay un código mejorado con la función Eliminar borde. Basado en una respuesta confusa .

 import UIKit enum viewBorder: String { case Left = "borderLeft" case Right = "borderRight" case Top = "borderTop" case Bottom = "borderBottom" } extension UIView { func addBorder(vBorder: viewBorder, color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.CGColor border.name = vBorder.rawValue switch vBorder { case .Left: border.frame = CGRectMake(0, 0, width, self.frame.size.height) case .Right: border.frame = CGRectMake(self.frame.size.width - width, 0, width, self.frame.size.height) case .Top: border.frame = CGRectMake(0, 0, self.frame.size.width, width) case .Bottom: border.frame = CGRectMake(0, self.frame.size.height - width, self.frame.size.width, width) } self.layer.addSublayer(border) } func removeBorder(border: viewBorder) { var layerForRemove: CALayer? for layer in self.layer.sublayers! { if layer.name == border.rawValue { layerForRemove = layer } } if let layer = layerForRemove { layer.removeFromSuperlayer() } } } 

Actualización: Swift 3

 import UIKit enum ViewBorder: String { case left, right, top, bottom } extension UIView { func add(border: ViewBorder, color: UIColor, width: CGFloat) { let borderLayer = CALayer() borderLayer.backgroundColor = color.cgColor borderLayer.name = border.rawValue switch border { case .left: borderLayer.frame = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height) case .right: borderLayer.frame = CGRect(x: self.frame.size.width - width, y: 0, width: width, height: self.frame.size.height) case .top: borderLayer.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: width) case .bottom: borderLayer.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: width) } self.layer.addSublayer(borderLayer) } func remove(border: ViewBorder) { guard let sublayers = self.layer.sublayers else { return } var layerForRemove: CALayer? for layer in sublayers { if layer.name == border.rawValue { layerForRemove = layer } } if let layer = layerForRemove { layer.removeFromSuperlayer() } } } 

O bien, la forma más fácil de usar es sobrecargar DrawRect, simplemente así:

 @interface TPActionSheetButton : UIButton @property (assign) BOOL drawsTopLine; @property (assign) BOOL drawsBottomLine; @property (assign) BOOL drawsRightLine; @property (assign) BOOL drawsLeftLine; @property (strong, nonatomic) UIColor * lineColor; @end @implementation TPActionSheetButton - (void) drawRect:(CGRect)rect { CGContextRef ctx = UIGraphicsGetCurrentContext(); CGContextSetLineWidth(ctx, 0.5f * [[UIScreen mainScreen] scale]); CGFloat red, green, blue, alpha; [self.lineColor getRed:&red green:&green blue:&blue alpha:&alpha]; CGContextSetRGBStrokeColor(ctx, red, green, blue, alpha); if(self.drawsTopLine) { CGContextBeginPath(ctx); CGContextMoveToPoint(ctx, CGRectGetMinX(rect), CGRectGetMinY(rect)); CGContextAddLineToPoint(ctx, CGRectGetMaxX(rect), CGRectGetMinY(rect)); CGContextStrokePath(ctx); } if(self.drawsBottomLine) { CGContextBeginPath(ctx); CGContextMoveToPoint(ctx, CGRectGetMinX(rect), CGRectGetMaxY(rect)); CGContextAddLineToPoint(ctx, CGRectGetMaxX(rect), CGRectGetMaxY(rect)); CGContextStrokePath(ctx); } if(self.drawsLeftLine) { CGContextBeginPath(ctx); CGContextMoveToPoint(ctx, CGRectGetMinX(rect), CGRectGetMinY(rect)); CGContextAddLineToPoint(ctx, CGRectGetMinX(rect), CGRectGetMaxY(rect)); CGContextStrokePath(ctx); } if(self.drawsRightLine) { CGContextBeginPath(ctx); CGContextMoveToPoint(ctx, CGRectGetMaxX(rect), CGRectGetMinY(rect)); CGContextAddLineToPoint(ctx, CGRectGetMaxX(rect), CGRectGetMaxY(rect)); CGContextStrokePath(ctx); } [super drawRect:rect]; } @end 

El problema con estos métodos de extensión es que cuando UIView / UIButton posteriormente ajusta su tamaño, no tiene posibilidad de cambiar el tamaño del CALayer para que coincida con el nuevo tamaño. Lo cual te dejará con un borde fuera de lugar. Descubrí que era mejor subclasificar mi UIButton; por supuesto, también podías subclase otras UIViews. Aquí hay un código:

 enum BorderedButtonSide { case Top, Right, Bottom, Left } class BorderedButton : UIButton { private var borderTop: CALayer? private var borderTopWidth: CGFloat? private var borderRight: CALayer? private var borderRightWidth: CGFloat? private var borderBottom: CALayer? private var borderBottomWidth: CGFloat? private var borderLeft: CALayer? private var borderLeftWidth: CGFloat? func setBorder(side: BorderedButtonSide, _ color: UIColor, _ width: CGFloat) { let border = CALayer() border.backgroundColor = color.CGColor switch side { case .Top: border.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: width) borderTop?.removeFromSuperlayer() borderTop = border borderTopWidth = width case .Right: border.frame = CGRect(x: frame.size.width - width, y: 0, width: width, height: frame.size.height) borderRight?.removeFromSuperlayer() borderRight = border borderRightWidth = width case .Bottom: border.frame = CGRect(x: 0, y: frame.size.height - width, width: frame.size.width, height: width) borderBottom?.removeFromSuperlayer() borderBottom = border borderBottomWidth = width case .Left: border.frame = CGRect(x: 0, y: 0, width: width, height: frame.size.height) borderLeft?.removeFromSuperlayer() borderLeft = border borderLeftWidth = width } layer.addSublayer(border) } override func layoutSubviews() { super.layoutSubviews() borderTop?.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: borderTopWidth!) borderRight?.frame = CGRect(x: frame.size.width - borderRightWidth!, y: 0, width: borderRightWidth!, height: frame.size.height) borderBottom?.frame = CGRect(x: 0, y: frame.size.height - borderBottomWidth!, width: frame.size.width, height: borderBottomWidth!) borderLeft?.frame = CGRect(x: 0, y: 0, width: borderLeftWidth!, height: frame.size.height) } } 

Swift 3 versión de la respuesta de Confile:

 import UIKit extension UIView { func addTopBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.cgColor border.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: width) self.layer.addSublayer(border) } func addRightBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.cgColor border.frame = CGRect(x: self.frame.size.width - width, y: 0, width: width, height: self.frame.size.height) self.layer.addSublayer(border) } func addBottomBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.cgColor border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: width) self.layer.addSublayer(border) } func addLeftBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.cgColor border.frame = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height) self.layer.addSublayer(border) } } 

Uso cuando se utiliza el diseño automático:

 class CustomView: UIView { override func awakeFromNib() { super.awakeFromNib() } override func layoutSubviews() { addBottomBorderWithColor(color: UIColor.white, width: 1) } } 

Escribí un método general que agregará un borde en los lados que desee en cualquier UIView . Puede definir grosor, color, márgenes y orden zOrder para cada lado.

 /* view: the view to draw border around thickness: thickness of the border on the given side color: color of the border on the given side margin: space between the border's outer edge and the view's frame edge on the given side. zOrder: defines the order to add the borders to the view. The borders will be added by zOrder from lowest to highest, thus making the highest priority border visible when two borders overlap at the corners. */ +(void) drawBorderAroundUIView:(UIView *) view thicknessLeft:(CGFloat) thicknessLeft colorLeft:(UIColor *)colorLeft marginLeft:(CGFloat) marginLeft zOrderLeft:(int) zOrderLeft thicknessRight:(CGFloat) thicknessRight colorRight:(UIColor *)colorRight marginRight:(CGFloat) marginRight zOrderRight:(int) zOrderRight thicknessTop:(CGFloat) thicknessTop colorTop:(UIColor *)colorTop marginTop:(CGFloat) marginTop zOrderTop:(int) zOrderTop thicknessBottom:(CGFloat) thicknessBottom colorBottom:(UIColor *)colorBottom marginBottom:(CGFloat) marginBottom zOrderBottom:(int) zOrderBottom{ //make margins be the outside edge and make positive margin represent a smaller rectangle marginBottom = -1 * marginBottom - thicknessBottom; marginTop = -1 * marginTop - thicknessTop; marginLeft = -1 * marginLeft - thicknessLeft; marginRight = -1 * marginRight - thicknessRight; //get reference points for corners CGPoint upperLeftCorner = CGPointZero; CGPoint lowerLeftCorner = CGPointMake(upperLeftCorner.x, upperLeftCorner.y + view.frame.size.height); CGPoint upperRightCorner = CGPointMake(upperLeftCorner.x + view.frame.size.width, upperLeftCorner.y); //left CALayer *leftBorder = [CALayer layer]; leftBorder.frame = CGRectMake(upperLeftCorner.x - thicknessLeft - marginLeft, upperLeftCorner.y - thicknessTop - marginTop, thicknessLeft, view.frame.size.height + marginTop + marginBottom + thicknessBottom + thicknessTop); leftBorder.backgroundColor = colorLeft.CGColor; //right CALayer *rightBorder = [CALayer layer]; rightBorder.frame = CGRectMake(upperRightCorner.x + marginRight, upperRightCorner.y - thicknessTop - marginTop, thicknessRight, view.frame.size.height + marginTop + marginBottom + thicknessBottom + thicknessTop); rightBorder.backgroundColor = colorRight.CGColor; //top CALayer *topBorder = [CALayer layer]; topBorder.frame = CGRectMake(upperLeftCorner.x - thicknessLeft - marginLeft, upperLeftCorner.y - thicknessTop - marginTop, view.frame.size.width + marginLeft + marginRight + thicknessLeft + thicknessRight, thicknessTop); topBorder.backgroundColor = colorTop.CGColor; //bottom CALayer *bottomBorder = [CALayer layer]; bottomBorder.frame = CGRectMake(upperLeftCorner.x - thicknessLeft - marginLeft, lowerLeftCorner.y + marginBottom, view.frame.size.width + marginLeft + marginRight + thicknessLeft + thicknessRight, thicknessBottom); bottomBorder.backgroundColor = colorBottom.CGColor; //define dictionary keys to be used for adding borders in order of zOrder NSString *borderDK = @"border"; NSString *zOrderDK = @"zOrder"; //storing borders in dictionaries in preparation to add them in order of zOrder NSDictionary *leftBorderDictionary = [NSDictionary dictionaryWithObjectsAndKeys:leftBorder, borderDK, [NSNumber numberWithInt:zOrderLeft], zOrderDK, nil]; NSDictionary *rightBorderDictionary = [NSDictionary dictionaryWithObjectsAndKeys:rightBorder, borderDK, [NSNumber numberWithInt:zOrderRight], zOrderDK, nil]; NSDictionary *topBorderDictionary = [NSDictionary dictionaryWithObjectsAndKeys:topBorder, borderDK, [NSNumber numberWithInt:zOrderTop], zOrderDK, nil]; NSDictionary *bottomBorderDictionary = [NSDictionary dictionaryWithObjectsAndKeys:bottomBorder, borderDK, [NSNumber numberWithInt:zOrderBottom], zOrderDK, nil]; NSMutableArray *borders = [NSMutableArray arrayWithObjects:leftBorderDictionary, rightBorderDictionary, topBorderDictionary, bottomBorderDictionary, nil]; //add borders in order of zOrder (lowest -> highest). Thus the highest zOrder will be added last so it will be on top. while (borders.count) { //look for the next lowest zOrder border to add NSDictionary *nextBorderToLayDown = [borders objectAtIndex:0]; for (int indexOfBorder = 0; indexOfBorder < borders.count; indexOfBorder++) { NSDictionary *borderAtIndex = [borders objectAtIndex:indexOfBorder]; if ([[borderAtIndex objectForKey:zOrderDK] intValue] < [[nextBorderToLayDown objectForKey:zOrderDK] intValue]) { nextBorderToLayDown = borderAtIndex; } } //add the border to the view [view.layer addSublayer:[nextBorderToLayDown objectForKey:borderDK]]; [borders removeObject:nextBorderToLayDown]; } } 

No es necesario que agregue una capa para cada borde, solo use una ruta bezier para dibujarlos una vez.

 CGRect rect = self.bounds; CGPoint destPoint[4] = {CGPointZero, (CGPoint){0, rect.size.height}, (CGPoint){rect.size.width, rect.size.height}, (CGPoint){rect.size.width, 0}}; BOOL position[4] = {_top, _left, _bottom, _right}; UIBezierPath *path = [UIBezierPath new]; [path moveToPoint:destPoint[3]]; for (int i = 0; i < 4; ++i) { if (position[i]) { [path addLineToPoint:destPoint[i]]; } else { [path moveToPoint:destPoint[i]]; } } CAShapeLayer *borderLayer = [CAShapeLayer new]; borderLayer.frame = self.bounds; borderLayer.path = path.CGPath; borderLayer.lineWidth = _borderWidth ?: 1 / [UIScreen mainScreen].scale; borderLayer.strokeColor = _borderColor.CGColor; borderLayer.fillColor = [UIColor clearColor].CGColor; [self.layer addSublayer:borderLayer]; 

Swift 4/3

Puedes usar esta solución debajo. Funciona en UIBezierPaths que son más livianos que las capas, lo que provoca tiempos de inicio rápidos. Es fácil de usar, mira las instrucciones a continuación.

 class ResizeBorderView: UIView { var color = UIColor.white var lineWidth: CGFloat = 1 var edges = [UIRectEdge](){ didSet { setNeedsDisplay() } } override func draw(_ rect: CGRect) { if edges.contains(.top) || edges.contains(.all){ let path = UIBezierPath() path.lineWidth = lineWidth color.setStroke() UIColor.blue.setFill() path.move(to: CGPoint(x: 0, y: 0 + lineWidth / 2)) path.addLine(to: CGPoint(x: self.bounds.width, y: 0 + lineWidth / 2)) path.stroke() } if edges.contains(.bottom) || edges.contains(.all){ let path = UIBezierPath() path.lineWidth = lineWidth color.setStroke() UIColor.blue.setFill() path.move(to: CGPoint(x: 0, y: self.bounds.height - lineWidth / 2)) path.addLine(to: CGPoint(x: self.bounds.width, y: self.bounds.height - lineWidth / 2)) path.stroke() } if edges.contains(.left) || edges.contains(.all){ let path = UIBezierPath() path.lineWidth = lineWidth color.setStroke() UIColor.blue.setFill() path.move(to: CGPoint(x: 0 + lineWidth / 2, y: 0)) path.addLine(to: CGPoint(x: 0 + lineWidth / 2, y: self.bounds.height)) path.stroke() } if edges.contains(.right) || edges.contains(.all){ let path = UIBezierPath() path.lineWidth = lineWidth color.setStroke() UIColor.blue.setFill() path.move(to: CGPoint(x: self.bounds.width - lineWidth / 2, y: 0)) path.addLine(to: CGPoint(x: self.bounds.width - lineWidth / 2, y: self.bounds.height)) path.stroke() } } } 
  1. Establezca la clase de UIView en ResizeBorderView
  2. Establezca el color y el ancho de línea usando yourview.color y yourview.lineWidth en su método viewDidAppear
  3. Establezca los bordes, por ejemplo: yourview.edges = [.right, .left] ([.all]) para todos
  4. Disfrute de inicio rápido y redimensionamiento de bordes

Swift 4

Basado en: https://stackoverflow.com/a/32821607/9980800

UIView + Border

 extension UIView { enum ViewBorder: String { case left, right, top, bottom } func add(Border border: ViewBorder, withColor color: UIColor = UIColor.lightGray, andWidth width: CGFloat = 1.0) { let borderView = UIView() borderView.backgroundColor = color borderView.translatesAutoresizingMaskIntoConstraints = false self.addSubview(borderView) NSLayoutConstraint.activate(getConstrainsFor(forView: borderView, WithBorderType: border, andWidth: width)) } private func getConstrainsFor(forView borderView: UIView, WithBorderType border: ViewBorder, andWidth width: CGFloat) -> [NSLayoutConstraint] { let height = borderView.heightAnchor.constraint(equalToConstant: width) let widthAnchor = borderView.widthAnchor.constraint(equalToConstant: width) let leading = borderView.leadingAnchor.constraint(equalTo: self.leadingAnchor) let trailing = borderView.trailingAnchor.constraint(equalTo: self.trailingAnchor) let top = borderView.topAnchor.constraint(equalTo: self.topAnchor) let bottom = borderView.bottomAnchor.constraint(equalTo: self.bottomAnchor) switch border { case .bottom: return [bottom, leading, trailing, height] case .top: return [top, leading, trailing, height] case .left: return [top, bottom, leading, widthAnchor] case .right: return [top, bottom, trailing, widthAnchor] } } 

}

Uso:-

 class ViewController: UIViewController { @IBOutlet weak var sampleView: UIView! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. sampleView.add(Border: .bottom) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } 

La respuesta más completa. https://github.com/oney/UIView-Border

 let rectangle = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 60)) rectangle.backgroundColor = UIColor.grayColor() view.addSubview(rectangle) rectangle.borderTop = Border(size: 3, color: UIColor.orangeColor(), offset: UIEdgeInsets(top: 0, left: -10, bottom: 0, right: -5)) rectangle.borderBottom = Border(size: 6, color: UIColor.redColor(), offset: UIEdgeInsets(top: 0, left: 10, bottom: 10, right: 0)) rectangle.borderLeft = Border(size: 2, color: UIColor.blueColor(), offset: UIEdgeInsets(top: 10, left: -10, bottom: 0, right: 0)) rectangle.borderRight = Border(size: 2, color: UIColor.greenColor(), offset: UIEdgeInsets(top: 10, left: 10, bottom: 0, right: 0)) 

enter image description here

Rápido

Crear la extensión UIView

 private var bottomLineColorAssociatedKey : UIColor = .black private var topLineColorAssociatedKey : UIColor = .black private var rightLineColorAssociatedKey : UIColor = .black private var leftLineColorAssociatedKey : UIColor = .black extension UIView { @IBInspectable var bottomLineColor: UIColor { get { if let color = objc_getAssociatedObject(self, &bottomLineColorAssociatedKey) as? UIColor { return color } else { return .black } } set { objc_setAssociatedObject(self, &bottomLineColorAssociatedKey, newValue, .OBJC_ASSOCIATION_RETAIN) } } @IBInspectable var bottomLineWidth: CGFloat { get { return self.bottomLineWidth } set { DispatchQueue.main.async { self.addBottomBorderWithColor(color: self.bottomLineColor, width: newValue) } } } @IBInspectable var topLineColor: UIColor { get { if let color = objc_getAssociatedObject(self, &topLineColorAssociatedKey) as? UIColor { return color } else { return .black } } set { objc_setAssociatedObject(self, &topLineColorAssociatedKey, newValue, .OBJC_ASSOCIATION_RETAIN) } } @IBInspectable var topLineWidth: CGFloat { get { return self.topLineWidth } set { DispatchQueue.main.async { self.addTopBorderWithColor(color: self.topLineColor, width: newValue) } } } @IBInspectable var rightLineColor: UIColor { get { if let color = objc_getAssociatedObject(self, &rightLineColorAssociatedKey) as? UIColor { return color } else { return .black } } set { objc_setAssociatedObject(self, &rightLineColorAssociatedKey, newValue, .OBJC_ASSOCIATION_RETAIN) } } @IBInspectable var rightLineWidth: CGFloat { get { return self.rightLineWidth } set { DispatchQueue.main.async { self.addRightBorderWithColor(color: self.rightLineColor, width: newValue) } } } @IBInspectable var leftLineColor: UIColor { get { if let color = objc_getAssociatedObject(self, &leftLineColorAssociatedKey) as? UIColor { return color } else { return .black } } set { objc_setAssociatedObject(self, &leftLineColorAssociatedKey, newValue, .OBJC_ASSOCIATION_RETAIN) } } @IBInspectable var leftLineWidth: CGFloat { get { return self.leftLineWidth } set { DispatchQueue.main.async { self.addLeftBorderWithColor(color: self.leftLineColor, width: newValue) } } } func addTopBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.name = "topBorderLayer" removePreviouslyAddedLayer(name: border.name ?? "") border.backgroundColor = color.cgColor border.frame = CGRect(x: 0, y : 0,width: self.frame.size.width, height: width) self.layer.addSublayer(border) } func addRightBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.name = "rightBorderLayer" removePreviouslyAddedLayer(name: border.name ?? "") border.backgroundColor = color.cgColor border.frame = CGRect(x: self.frame.size.width - width, y: 0, width : width, height :self.frame.size.height) self.layer.addSublayer(border) } func addBottomBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.name = "bottomBorderLayer" removePreviouslyAddedLayer(name: border.name ?? "") border.backgroundColor = color.cgColor border.frame = CGRect(x: 0, y: self.frame.size.height - width,width : self.frame.size.width,height: width) self.layer.addSublayer(border) } func addLeftBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.name = "leftBorderLayer" removePreviouslyAddedLayer(name: border.name ?? "") border.backgroundColor = color.cgColor border.frame = CGRect(x:0, y:0,width : width, height : self.frame.size.height) self.layer.addSublayer(border) } func removePreviouslyAddedLayer(name : String) { if self.layer.sublayers?.count ?? 0 > 0 { self.layer.sublayers?.forEach { if $0.name == name { $0.removeFromSuperlayer() } } } } } 

C objective

Crear clase de categoría de UIView

UIView + Border.h

 #import  #import  @interface UIView (Border) @property (nonatomic) IBInspectable UIColor *topLineColor; @property (nonatomic) IBInspectable CGFloat topLineWidth; @property (nonatomic) IBInspectable UIColor *bottomLineColor; @property (nonatomic) IBInspectable CGFloat bottomLineWidth; @property (nonatomic) IBInspectable UIColor *rightLineColor; @property (nonatomic) IBInspectable CGFloat rightLineWidth; @property (nonatomic) IBInspectable UIColor *leftLineColor; @property (nonatomic) IBInspectable CGFloat leftLineWidth; - (void)addBottomBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth; - (void)addLeftBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth; - (void)addRightBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth; - (void)addTopBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth; @end 

UIView+Border.m

 #import "UIView+Utility.h" #import  static char bottomLineColorKey,topLineColorKey,rightLineColorKey,leftLineColorKey; @implementation UIView(Border) @dynamic bottomLineWidth,topLineWidth,rightLineWidth,leftLineWidth; // for Bottom Line - (UIColor *)bottomLineColor { return objc_getAssociatedObject(self, &bottomLineColorKey); } - (void)setBottomLineColor:(UIColor *)bottomLineColor { objc_setAssociatedObject(self, &bottomLineColorKey, bottomLineColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -(void)setBottomLineWidth:(CGFloat)bottomLineWidth { dispatch_async(dispatch_get_main_queue(), ^{ [self addBottomBorderWithColor:[self bottomLineColor] andWidth:bottomLineWidth]; }); } // for top Line - (UIColor *)topLineColor { return objc_getAssociatedObject(self, &topLineColorKey); } - (void)setTopLineColor:(UIColor *)topLineColor { objc_setAssociatedObject(self, &topLineColorKey, topLineColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void)setTopLineWidth:(CGFloat)topLineWidth{ dispatch_async(dispatch_get_main_queue(), ^{ [self addTopBorderWithColor:[self topLineColor] andWidth:topLineWidth]; }); } // for right Line - (UIColor *)rightLineColor { return objc_getAssociatedObject(self, &rightLineColorKey); } -(void)setRightLineColor:(UIColor *)rightLineColor { objc_setAssociatedObject(self, &rightLineColorKey, rightLineColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -(void)setRightLineWidth:(CGFloat)rightLineWidth{ dispatch_async(dispatch_get_main_queue(), ^{ [self addRightBorderWithColor:[self rightLineColor] andWidth:rightLineWidth]; }); } // for left Line -(UIColor *)leftLineColor { return objc_getAssociatedObject(self, &leftLineColorKey); } -(void)setLeftLineColor:(UIColor *)leftLineColor{ objc_setAssociatedObject(self, &leftLineColorKey, leftLineColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -(void)setLeftLineWidth:(CGFloat)leftLineWidth{ dispatch_async(dispatch_get_main_queue(), ^{ [self addLeftBorderWithColor:[self leftLineColor] andWidth:leftLineWidth]; }); } - (void)addTopBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth { CALayer *border = [CALayer layer]; border.name = @"topBorderLayer"; [self removePreviouslyAddedLayer:border.name]; border.backgroundColor = color.CGColor; border.frame = CGRectMake(0, 0, self.frame.size.width, borderWidth); [self.layer addSublayer:border]; } - (void)addBottomBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth { CALayer *border = [CALayer layer]; border.name = @"bottomBorderLayer"; [self removePreviouslyAddedLayer:border.name]; border.backgroundColor = color.CGColor; border.frame = CGRectMake(0, self.frame.size.height - borderWidth, self.frame.size.width, borderWidth); [self.layer addSublayer:border]; } - (void)addLeftBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth { CALayer *border = [CALayer layer]; border.name = @"leftBorderLayer"; [self removePreviouslyAddedLayer:border.name]; border.backgroundColor = color.CGColor; border.frame = CGRectMake(0, 0, borderWidth, self.frame.size.height); [self.layer addSublayer:border]; } - (void)addRightBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth { CALayer *border = [CALayer layer]; border.name = @"rightBorderLayer"; [self removePreviouslyAddedLayer:border.name]; border.backgroundColor = color.CGColor; border.frame = CGRectMake(self.frame.size.width - borderWidth, 0, borderWidth, self.frame.size.height); [self.layer addSublayer:border]; } - (void)removePreviouslyAddedLayer:(NSString *)name { if (self.layer.sublayers.count > 0) { for (CALayer *layer in self.layer.sublayers) { if ([layer.name isEqualToString:name]) { [layer removeFromSuperlayer]; } } } } @end 

Usage :- Select any control from storyboard, then show attribute inspector (Right side) You will see below image Example.(Note : Border only appear at run time.)

Now You can set any side of border colour and width.

Swift 4 extension with border width and color. ¡Funciona genial!

 @IBDesignable final class SideBorders: UIView { @IBInspectable var topColor: UIColor = UIColor.clear @IBInspectable var topWidth: CGFloat = 0 @IBInspectable var rightColor: UIColor = UIColor.clear @IBInspectable var rightWidth: CGFloat = 0 @IBInspectable var bottomColor: UIColor = UIColor.clear @IBInspectable var bottomWidth: CGFloat = 0 @IBInspectable var leftColor: UIColor = UIColor.clear @IBInspectable var leftWidth: CGFloat = 0 override func draw(_ rect: CGRect) { let topBorder = CALayer() topBorder.backgroundColor = topColor.cgColor topBorder.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: topWidth) self.layer.addSublayer(topBorder) let rightBorder = CALayer() rightBorder.backgroundColor = rightColor.cgColor rightBorder.frame = CGRect(x: self.frame.size.width - rightWidth, y: 0, width: rightWidth, height: self.frame.size.height) self.layer.addSublayer(rightBorder) let bottomBorder = CALayer() bottomBorder.backgroundColor = bottomColor.cgColor bottomBorder.frame = CGRect(x: 0, y: self.frame.size.height - bottomWidth, width: self.frame.size.width, height: bottomWidth) self.layer.addSublayer(bottomBorder) let leftBorder = CALayer() leftBorder.backgroundColor = leftColor.cgColor leftBorder.frame = CGRect(x: 0, y: self.frame.size.height - leftWidth, width: self.frame.size.width, height: leftWidth) self.layer.addSublayer(leftBorder) } 

}

Swift 4

Based on https://stackoverflow.com/a/32513578/5391914

 import UIKit enum viewBorder: String { case Left = "borderLeft" case Right = "borderRight" case Top = "borderTop" case Bottom = "borderBottom" } extension UIView { func addBorder(vBorders: [viewBorder], color: UIColor, width: CGFloat) { vBorders.forEach { vBorder in let border = CALayer() border.backgroundColor = color.cgColor border.name = vBorder.rawValue switch vBorder { case .Left: border.frame = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height) case .Right: border.frame = CGRect(x:self.frame.size.width - width, y: 0, width: width, height: self.frame.size.height) case .Top: border.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: width) case .Bottom: border.frame = CGRect(x: 0, y: self.frame.size.height - width , width: self.frame.size.width, height: width) } self.layer.addSublayer(border) } } } 

Swift 4 That’s work fine by copy this UIView extension

 extension UIView { final func addBorder(edges: UIRectEdge, color: UIColor = UIColor.white, thickness: CGFloat = 1.0) -> [UIView] { var borders = [UIView]() func border() -> UIView { let border = UIView(frame: CGRect.zero) border.backgroundColor = color border.translatesAutoresizingMaskIntoConstraints = false return border } if edges.contains(.top) || edges.contains(.all) { let top = border() addSubview(top) addConstraints( NSLayoutConstraint.constraints(withVisualFormat: "V:|-(0)-[top(==thickness)]", options: [], metrics: ["thickness": thickness], views: ["top": top])) addConstraints( NSLayoutConstraint.constraints(withVisualFormat: "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.constraints(withVisualFormat: "H:|-(0)-[left(==thickness)]", options: [], metrics: ["thickness": thickness], views: ["left": left])) addConstraints( NSLayoutConstraint.constraints(withVisualFormat: "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.constraints(withVisualFormat: "H:[right(==thickness)]-(0)-|", options: [], metrics: ["thickness": thickness], views: ["right": right])) addConstraints( NSLayoutConstraint.constraints(withVisualFormat: "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.constraints(withVisualFormat: "V:[bottom(==thickness)]-(0)-|", options: [], metrics: ["thickness": thickness], views: ["bottom": bottom])) addConstraints( NSLayoutConstraint.constraints(withVisualFormat: "H:|-(0)-[bottom]-(0)-|", options: [], metrics: nil, views: ["bottom": bottom])) borders.append(bottom) } return borders } } 

In your ViewController, you can use it by:

 _ = view.addBorder(edges: .bottom, color: UIColor.lightGray, thickness: 0.3) 

You can change “.bottom” to top, left, or right, or array “[.bottom, .top]” that suit your work.