Como adicionar uma borda na parte superior de um UIView

144

Minha pergunta está no título.

Eu não sei como adicionar uma borda em um lado específico, superior ou inferior, qualquer lado ... layer.borderdesenha a borda para toda a exibição ...

homem na Lua
fonte
possível duplicata da borda inferior
Cœur

Respostas:

200

Considero subclassificar UIViewe substituir um drawRectexagero aqui. Por que não adicionar uma extensão UIViewe adicionar subvisões de borda?

@discardableResult
func 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

Com esse código, você também não está vinculado à sua subclasse, pode aplicá-lo a tudo e qualquer coisa que herda UIView- reutilizável no seu projeto e em qualquer outro. Passe outros argumentos para seus métodos para definir outras cores e larguras. Muitas opções

Adam Waite
fonte
4
A única desvantagem aqui é que ela não pode ser redimensionada.
31814 Peter PWeese
2
Pode usar um UIView e adicionar restrições de layout automático. Mesmo princípio.
Adam Waite
1
@PeterDeWeese Isso também não é uma desvantagem - se você deseja controlar o tamanho da borda, tudo o que você precisa fazer é algo como: - (void) addUpperBorderWithSize: (CGFloat) size E substitua a constante pelo parâmetro na função . O mesmo vale para outros parâmetros, como cores.
o.shnn
1
@ AdamWaite, essa variante de layout automático parece muito boa. Obrigado !
manonthemoon
2
Obrigado por compartilhar, eu faço uma versão do Objective-C com restrição de layout automático aqui .
Itachi
99

Adicionado recurso para cantos arredondados à postagem original de Adam Waite e às várias edições

Importante !: Não se esqueça de adicionar 'label.layoutIfNeeded ()' logo antes de chamar 'addborder' como comentado anteriormente

Nota: Eu testei isso apenas no UILabels.

extensão 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 startAngle: CGFloat = .pi; var endAngle: CGFloat = .pi*3/2

    switch corner {
    case .bottomLeft: startAngle = .pi/2; endAngle = .pi

    case .bottomRight:
        x = width - 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: height / 2),
                                  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)
}

}

Dan
fonte
4
Este código não está trabalhando em Swift 3 por alguma razão, depois de mudar a assinatura CGRectMake eo CGColor por Swift 3.
EmbCoder
Há um bom artigo aqui medium.com/swift-programming/... que fala sobre o que você precisa fazer para Swift 3.
Micah Montoya
2
Alguma sorte em implementar isso no Swift 3?
Marquavious Draggon
7
Para fazê-lo funcionar, você precisa adicionar view.layoutIfNeeded()um pouco antes de ligar view.layer.addBorder(...). Em seguida, ele trabalha na Swift 3
Adam Studenic
está funcionando bem. adicione a linha neste métodooverride func viewDidLayoutSubviews()
Antony Raphel 12/06
60

A melhor maneira para mim é uma categoria no UIView, mas em adding viewsvez do CALayers, para que possamostake advantage of AutoresizingMasks garantir que as bordas sejam redimensionadas junto com a superview.

Objetivo 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)
}
Pauls
fonte
7
Essa é de longe uma das melhores soluções para esse problema. A maioria das outras soluções oferecidas NÃO respeita as alterações de exibição (e, portanto, a rotação do dispositivo ou as exibições divididas). Este faz.
Womble
1
como isso pode ser adaptado para suportar cantos arredondados?
Ntaj 8/11
29

Swift 3.0

Swift 4.1

extension CALayer {

  func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {

    let border = CALayer()

    switch edge {
    case UIRectEdge.top:
        border.frame = CGRect(x: 0, y: 0, width: frame.width, height: thickness)

    case UIRectEdge.bottom:
        border.frame = CGRect(x:0, y: frame.height - thickness, width: frame.width, height:thickness)

    case UIRectEdge.left:
        border.frame = CGRect(x:0, y:0, width: thickness, height: frame.height)

    case UIRectEdge.right:
        border.frame = CGRect(x: frame.width - thickness, y: 0, width: thickness, height: frame.height)

    default: do {}
    }

    border.backgroundColor = color.cgColor

    addSublayer(border)
 }
}
AleyRobotics
fonte
1
Isso não funciona quando a visualização é redimensionada, por exemplo, mudança de orientação.
Nestor
Se você estiver usando o layout baseado em quadro, precisará implementar layoutSubviews e recalcular todos os quadros de subvisão (ou subcamada) lá
trapper
22

Subclasse UIViewe implemente drawRect:em sua subclasse, por exemplo:

Objetivo-c

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextMoveToPoint(context, CGRectGetMinX(rect), CGRectGetMinY(rect));
    CGContextAddLineToPoint(context, CGRectGetMaxX(rect), CGRectGetMinY(rect));
    CGContextSetStrokeColorWithColor(context, [[UIColor redColor] CGColor] );
    CGContextSetLineWidth(context, 2.0);
    CGContextStrokePath(context);
}

Swift 4

override func draw(_ rect: CGRect) {

    let cgContext = UIGraphicsGetCurrentContext()
    cgContext?.move(to: CGPoint(x: rect.minX, y: rect.minY))
    cgContext?.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
    cgContext?.setStrokeColor(UIColor.red.cgColor)
    cgContext?.setLineWidth(2.0)
    cgContext?.strokePath()
}

Isso desenha uma linha vermelha de 2 pixels como uma borda superior. Todas as outras variações mencionadas são deixadas como um exercício trivial para o leitor.

Recomenda-se o Guia de Programação em Quartzo 2D .

FluffulousChimp
fonte
É o único caminho para isso ??? Portanto, se eu precisar fazer uma exibição com uma borda superior e outra com borda inferior e outra exibição ... Quero dizer sempre uma exibição específica com uma borda ou duas, terei que criar uma subclasse para cada caso específico ??? Eu não sei se é muito bom para criar classe apenas para adicionar bordas ...
manonthemoon
1
Não. Pense nisso. Sua UIViewsubclasse pode ter uma propriedade que determina quais bordas são drawRect:desenhadas. Esta propriedade pode ser definida com NS_OPTIONSpara que ela defina uma máscara de bits como a UIViewAutoresizingmáscara de bits. Se, por qualquer motivo, você realmente se opuser a subclassificar UIView, basta adicionar uma subvisão pequena de 1 a 2 pixels de altura (ou ampla) e fornecer as dimensões que deseja simular uma borda.
FluffulousChimp
Ei, eu fiz uma Swift subclasse inspirado por este código, espero que você goste: gist.github.com/asiviero/4f52ab7dea7d9252a64c
asiviero
19

Código da resposta selecionada, caso alguém queira.

NOTA: Isso não funciona com o layout automático (também conhecido como dispositivo rotativo para paisagem etc.).

Primeiro defina uma espessura:

NSInteger borderThickness = 1;

Em seguida, basta copiar, use qualquer um ou todos esses para definir a borda que você deseja definir.

Borda superior

UIView *topBorder = [UIView new];
topBorder.backgroundColor = [UIColor lightGrayColor];
topBorder.frame = CGRectMake(0, 0, myView.frame.size.width, borderThickness);
[myView addSubview:topBorder];

Borda inferior

UIView *bottomBorder = [UIView new];
bottomBorder.backgroundColor = [UIColor lightGrayColor];
bottomBorder.frame = CGRectMake(0, myView.frame.size.height - borderThickness, myView.frame.size.width, borderThickness);
[myView addSubview:bottomBorder];

Borda esquerda

UIView *leftBorder = [UIView new];
leftBorder.backgroundColor = [UIColor lightGrayColor];
leftBorder.frame = CGRectMake(0, 0, borderThickness, myView.frame.size.height);
[myView addSubview:leftBorder];

Borda direita

UIView *rightBorder = [UIView new];
rightBorder.backgroundColor = [UIColor lightGrayColor];
rightBorder.frame = CGRectMake(myView.frame.size.width - borderThickness, 0, borderThickness, myView.frame.size.height);
[myView addSubview:rightBorder];
Travis M.
fonte
Solução simples assim ... com uma UIView sobrecarga comparando a nu CALayer
Soberman
depois de ler todas as outras soluções, pensei em adicionar uma pequena visão também. Todo esse trabalho para uma fronteira! Esta solução requer apenas alguns pinos para funcionar também no layout automático. Muito mais fácil de entender.
Noobmcgoobs
Obrigado, adicionei uma observação acima para que os codificadores só sejam usados ​​se o aplicativo não girar.
Travis M.
15

Pergunta antiga, mas a solução de pagamento automático com ajustes de borda de tempo de execução ainda está faltando.

borders(for: [.left, .bottom], width: 2, color: .red)

A seguinte extensão UIView adicionará a borda apenas nas bordas fornecidas. Se você alterar as bordas no tempo de execução, as bordas serão ajustadas de acordo.

extension UIView {
    func borders(for edges:[UIRectEdge], width:CGFloat = 1, color: UIColor = .black) {

        if edges.contains(.all) {
            layer.borderWidth = width
            layer.borderColor = color.cgColor
        } else {
            let allSpecificBorders:[UIRectEdge] = [.top, .bottom, .left, .right]

            for edge in allSpecificBorders {
                if let v = viewWithTag(Int(edge.rawValue)) {
                    v.removeFromSuperview()
                }

                if edges.contains(edge) {
                    let v = UIView()
                    v.tag = Int(edge.rawValue)
                    v.backgroundColor = color
                    v.translatesAutoresizingMaskIntoConstraints = false
                    addSubview(v)

                    var horizontalVisualFormat = "H:"
                    var verticalVisualFormat = "V:"

                    switch edge {
                    case UIRectEdge.bottom:
                        horizontalVisualFormat += "|-(0)-[v]-(0)-|"
                        verticalVisualFormat += "[v(\(width))]-(0)-|"
                    case UIRectEdge.top:
                        horizontalVisualFormat += "|-(0)-[v]-(0)-|"
                        verticalVisualFormat += "|-(0)-[v(\(width))]"
                    case UIRectEdge.left:
                        horizontalVisualFormat += "|-(0)-[v(\(width))]"
                        verticalVisualFormat += "|-(0)-[v]-(0)-|"
                    case UIRectEdge.right:
                        horizontalVisualFormat += "[v(\(width))]-(0)-|"
                        verticalVisualFormat += "|-(0)-[v]-(0)-|"
                    default:
                        break
                    }

                    self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: horizontalVisualFormat, options: .directionLeadingToTrailing, metrics: nil, views: ["v": v]))
                    self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: verticalVisualFormat, options: .directionLeadingToTrailing, metrics: nil, views: ["v": v]))
                }
            }
        }
    }
}
seeppp
fonte
13

Versão rápida:

var myView = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
myView.backgroundColor = UIColor.yellowColor() 

var border = CALayer()
border.backgroundColor = UIColor.lightGrayColor()
border.frame = CGRect(x: 0, y: 0, width: myView.frame.width, height: 0.5)

myView.layer.addSublayer(border)

Editar: Para versões atualizadas, verifique meu repositório aqui: https://github.com/goktugyil/EZSwiftExtensions/blob/master/Sources/UIViewExtensions.swift

Veja as partes addBorder

Esqarrouth
fonte
4

Peguei as respostas de Adam Waite e Pauls e as combinei. Também adicionei a possibilidade de unir as arestas selecionadas, portanto, você precisa chamar apenas uma função como esta:

[self.view addBordersToEdge:(UIRectEdgeLeft|UIRectEdgeRight)
                  withColor:[UIColor grayColor]
                   andWidth:1.0];

ou então:

[self.view addBordersToEdge:(UIRectEdgeAll)
                  withColor:[UIColor grayColor]
                   andWidth:1.0];

O que você precisa implementar é uma categoria no UIView, conforme sugerido em outras respostas com a seguinte implementação:

- (void)addBordersToEdge:(UIRectEdge)edge withColor:(UIColor *)color andWidth:(CGFloat) borderWidth {
    if (edge & UIRectEdgeTop) {
        UIView *border = [UIView new];
        border.backgroundColor = color;
        [border setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin];
        border.frame = CGRectMake(0, 0, self.frame.size.width, borderWidth);
        [self addSubview:border];
    }

    if (edge & UIRectEdgeLeft) {
        UIView *border = [UIView new];
        border.backgroundColor = color;
        border.frame = CGRectMake(0, 0, borderWidth, self.frame.size.height);
        [border setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleRightMargin];
        [self addSubview:border];
    }

    if (edge & UIRectEdgeBottom) {
        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];
    }

    if (edge & UIRectEdgeRight) {
        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];
    }
}
Maco03
fonte
4

// MARK: - Adicionar borda esquerda para exibição

(void)prefix_addLeftBorder:(UIView *) viewName
{
    CALayer *leftBorder = [CALayer layer];
    leftBorder.backgroundColor = [UIColor colorWithRed:221/255.0f green:221/255.0f blue:221/255.0f alpha:1.0f].CGColor;
    leftBorder.frame = CGRectMake(0,0,1.0,viewName.frame.size.height);
    [viewName.layer addSublayer:leftBorder];
}

// MARK: - Adicionar borda direita para exibição

(void)prefix_addRightBorder:(UIView *) viewName
{
    CALayer *rightBorder = [CALayer layer];
    rightBorder.backgroundColor = [UIColor colorWithRed:221/255.0f green:221/255.0f blue:221/255.0f alpha:1.0f].CGColor;
    rightBorder.frame = CGRectMake(viewName.frame.size.width - 1.0,0,1.0,viewName.frame.size.height);
    [viewName.layer addSublayer:rightBorder];
}

// MARK: - Adicionar borda inferior para exibição

(void)prefix_addbottomBorder:(UIView *) viewName
{
    CALayer *bottomBorder = [CALayer layer];
    bottomBorder.backgroundColor = [UIColor colorWithRed:221/255.0f green:221/255.0f blue:221/255.0f alpha:1.0f].CGColor;
    bottomBorder.frame = CGRectMake(0,viewName.frame.size.height - 1.0,viewName.frame.size.width,1.0);
    [viewName.layer addSublayer:bottomBorder];
}
Priyanka Jadhav
fonte
4

Swift 4.2 e AutoLayout

Passei pelas soluções oferecidas. Muitos são baseados em quadros Essa é uma extensão simples que funciona com AutoLayout - use View em vez de Layer para garantir que possamos usar AutoLayout - Single subview com 4 restrições

Use da seguinte maneira:

self.addBorder(.bottom, color: .lightGray, thickness: 0.5)


extension UIView {
    func addBorder(_ edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
        let subview = UIView()
        subview.translatesAutoresizingMaskIntoConstraints = false
        subview.backgroundColor = color
        self.addSubview(subview)
        switch edge {
        case .top, .bottom:
            subview.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
            subview.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
            subview.heightAnchor.constraint(equalToConstant: thickness).isActive = true
            if edge == .top {
                subview.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
            } else {
                subview.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
            }
        case .left, .right:
            subview.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
            subview.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
            subview.widthAnchor.constraint(equalToConstant: thickness).isActive = true
            if edge == .left {
                subview.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
            } else {
                subview.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
            }
        default:
            break
        }
    }
}
eharo2
fonte
3

Fiz algumas alterações na resposta de Dan para poder adicionar bordas a várias bordas com um comando:

infoView.addBorder(toEdges: [.left, .bottom, .right], color: borderColor, thickness: 1)

Aqui está o código completo:

extension UIView {
    func addBorder(toEdges edges: UIRectEdge, color: UIColor, thickness: CGFloat) {

        func addBorder(toEdge edges: UIRectEdge, color: UIColor, thickness: CGFloat) {
            let border = CALayer()
            border.backgroundColor = color.cgColor

            switch edges {
            case .top:
                border.frame = CGRect(x: 0, y: 0, width: frame.width, height: thickness)
            case .bottom:
                border.frame = CGRect(x: 0, y: frame.height - thickness, width: frame.width, height: thickness)
            case .left:
                border.frame = CGRect(x: 0, y: 0, width: thickness, height: frame.height)
            case .right:
                border.frame = CGRect(x: frame.width - thickness, y: 0, width: thickness, height: frame.height)
            default:
                break
            }

            layer.addSublayer(border)
        }

        if edges.contains(.top) || edges.contains(.all) {
            addBorder(toEdge: .top, color: color, thickness: thickness)
        }

        if edges.contains(.bottom) || edges.contains(.all) {
            addBorder(toEdge: .bottom, color: color, thickness: thickness)
        }

        if edges.contains(.left) || edges.contains(.all) {
            addBorder(toEdge: .left, color: color, thickness: thickness)
        }

        if edges.contains(.right) || edges.contains(.all) {
            addBorder(toEdge: .right, color: color, thickness: thickness)
        }
    }
}
NSExceptional
fonte
2

Construindo fora de de NSBum resposta, tomei uma abordagem semelhante e criou esta subclasse UIView simples para que ele funcione no Interface Builder e trabalha com restrições: ligação github
Usando CGContextFillRect em vez de CGContextStrokePath, eu era capaz de manter previsivelmente as linhas completamente sólidas e dentro os limites da vista.

Aqui está o meu post sobre isso: http://natrosoft.com/?p=55

- Basicamente, basta inserir um UIView no Interface Builder e alterar seu tipo de classe para NAUIViewWithBorders.
- Então, no viewDidLoad do seu VC, faça algo como:

/* For a top border only ———————————————- */
self.myBorderView.borderColorTop = [UIColor redColor];
self.myBorderView..borderWidthsAll = 1.0f;

/* For borders with different colors and widths ————————— */
self.myBorderView.borderWidths = UIEdgeInsetsMake(2.0, 4.0, 6.0, 8.0);
self.myBorderView.borderColorTop = [UIColor blueColor];
self.myBorderView.borderColorRight = [UIColor redColor];
self.myBorderView.borderColorBottom = [UIColor greenColor];
self.myBorderView.borderColorLeft = [UIColor darkGrayColor];

Aqui está um link direto para o arquivo .m para que você possa ver a implementação. Há um projeto de demonstração também. Espero que isso ajude alguém :)

n8tr
fonte
2

Minha resposta para uma pergunta semelhante: https://stackoverflow.com/a/27141956/435766 Pessoalmente, prefiro seguir o caminho da categoria nesse caso, pois quero poder usá-lo em qualquer subclasse do UIView.

P. até
fonte
2
extension UIView {

    func addBorder(edge: UIRectEdge, color: UIColor, borderWidth: CGFloat) {

        let seperator = UIView()
        self.addSubview(seperator)
        seperator.translatesAutoresizingMaskIntoConstraints = false

        seperator.backgroundColor = color

        if edge == .top || edge == .bottom
        {
            seperator.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true
            seperator.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true
            seperator.heightAnchor.constraint(equalToConstant: borderWidth).isActive = true

            if edge == .top
            {
                seperator.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
            }
            else
            {
                seperator.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
            }
        }
        else if edge == .left || edge == .right
        {
            seperator.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
            seperator.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
            seperator.widthAnchor.constraint(equalToConstant: borderWidth).isActive = true

            if edge == .left
            {
                seperator.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true
            }
            else
            {
                seperator.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true
            }
        }
    }

}
Ali Seymen
fonte
2

Aqui está uma solução simples. Adicione uma etiqueta à sua UIView, limpe o texto na etiqueta e defina a cor de fundo da etiqueta como a sua borda. Defina a origem (x,y)do seu rótulo como a origem (x,y)da sua exibição. e defina a largura do rótulo como a sua UIView, defina a altura como 1 ou 2 (para a altura da borda na parte superior da sua UIView). E isso deve fazer o truque.

Se Pollavith
fonte
28
Nenhum ponto em ter um rótulo, em seguida, pode muito bem ser outro UIView
maor10
1
Isso realmente não responde à questão de adicionar apenas a borda superior a uma visão geral. Esta é uma solução rápida que provavelmente não funcionaria para problemas mais complexos.
simon_smiley
1
veja isso: stackoverflow.com/questions/7022656/…
Roland Kákonyi
2

Versão Swift 3

extension UIView {
    enum ViewSide {
        case Top, Bottom, Left, Right
    }

    func addBorder(toSide side: ViewSide, withColor color: UIColor, andThickness thickness: CGFloat) {

        let border = CALayer()
        border.backgroundColor = color.cgColor

        switch side {
        case .Top:
            border.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: thickness)
        case .Bottom:
            border.frame = CGRect(x: 0, y: frame.size.height - thickness, width: frame.size.width, height: thickness)
        case .Left:
            border.frame = CGRect(x: 0, y: 0, width: thickness, height: frame.size.height)
        case .Right:
            border.frame = CGRect(x: frame.size.width - thickness, y: 0, width: thickness, height: frame.size.height)
        }

        layer.addSublayer(border)
    }
}

Para definir a borda correspondente, você deve substituir o método viewDidLayoutSubviews () :

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    yourView.addBorder(toSide: UIView.ViewSide.Top, withColor: UIColor.lightGray, andThickness: 1)
karjaubayev
fonte
Será chamado de 'addSublayer' sempre que o layout for atualizado?
byJeevan
1

Basta postar aqui para ajudar alguém que deseja adicionar bordas. Fiz algumas alterações na resposta aceita aqui, etiqueta rápida apenas com a borda esquerda . Largura alterada no caso UIRectEdge.Topde CGRectGetHeight(self.frame)para CGRectGetWidth(self.frame)e no caso UIRectEdge.Bottomde UIScreen.mainScreen().bounds.widthparaCGRectGetWidth(self.frame) obter fronteiras corretamente. Usando o Swift 2.

Finalmente, a extensão é:

extension CALayer {

func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {

    let border = CALayer();

    switch edge {
    case UIRectEdge.Top:
        border.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), thickness); 
        break
    case UIRectEdge.Bottom:
        border.frame = CGRectMake(0, CGRectGetHeight(self.frame) - thickness, CGRectGetWidth(self.frame), thickness)
        break
    case UIRectEdge.Left:
        border.frame = CGRectMake(0, 0, thickness, CGRectGetHeight(self.frame))
        break
    case UIRectEdge.Right:
        border.frame = CGRectMake(CGRectGetWidth(self.frame) - thickness, 0, thickness, CGRectGetHeight(self.frame))
        break
    default:
        break
    }

    border.backgroundColor = color.CGColor;

    self.addSublayer(border)
}

}
Kavisha
fonte
1

Caso alguém precise da versão do Xamarin:

public static class UIUtils
{
    public static void AddBorder(this CALayer cALayer, UIRectEdge edge, UIColor color, float thickness)
    {

        var border = new CALayer();
        switch (edge) 
        {
            case UIRectEdge.Top:
                border.Frame = new CGRect(0, 0, cALayer.Frame.Width, height: thickness);
                break;
            case UIRectEdge.Bottom:
                border.Frame = new CGRect(0, cALayer.Frame.Height - thickness, width: cALayer.Frame.Width, height: thickness);
                break;
            case UIRectEdge.Left:
                border.Frame = new CGRect(0, 0, width: thickness, height: cALayer.Frame.Height);
                break;
            case UIRectEdge.Right:
                border.Frame = new CGRect(cALayer.Frame.Width - thickness, y: 0, width: thickness, height: cALayer.Frame.Height);
                break;
            default: break;
        }
        border.BackgroundColor = color.CGColor;
        cALayer.AddSublayer(border);
    }
}
Matriz
fonte
1

Aqui está uma versão Swift 4 da resposta de Pauls

func addTopBorder(color: UIColor, thickness: CGFloat) {
    let border = UIView()
    border.backgroundColor = color
    border.autoresizingMask = [.flexibleWidth, .flexibleBottomMargin]
    border.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: thickness)
    addSubview(border)
}

func addBottomBorder(color: UIColor, thickness: CGFloat) {
    let border = UIView()
    border.backgroundColor = color
    border.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
    border.frame = CGRect(x: 0, y: frame.size.height - thickness, width: frame.size.width, height: thickness)
    addSubview(border)
}

func addLeftBorder(color: UIColor, thickness: CGFloat) {
    let border = UIView()
    border.backgroundColor = color
    border.autoresizingMask = [.flexibleHeight, .flexibleRightMargin]
    border.frame = CGRect(x: 0, y: 0, width: thickness, height: frame.size.height)
    addSubview(border)
}

func addRightBorder(color: UIColor, thickness: CGFloat) {
    let border = UIView()
    border.backgroundColor = color
    border.autoresizingMask = [.flexibleHeight, .flexibleLeftMargin]
    border.frame = CGRect(x: frame.size.width - thickness, y: 0, width: thickness, height: frame.size.height)
    addSubview(border)
}
NSExceptional
fonte
1

Inspirado por @Addison, reescrevi a extensão sem o uso de qualquer estrutura de terceiros, pois ele usou o SnapKit e o CocoaLumberjack.

Como na abordagem @Addisons, também estou removendo bordas adicionadas anteriormente, portanto essa implementação deve ser agradável com visualizações reutilizáveis ​​como células de tabela e células de coleção.

fileprivate class BorderView: UIView {} // dummy class to help us differentiate among border views and other views
                                        // to enabling us to remove existing borders and place new ones

extension UIView {

    func setBorders(toEdges edges: [UIRectEdge], withColor color: UIColor, inset: CGFloat = 0, thickness: CGFloat) {
        // Remove existing edges
        for view in subviews {
            if view is BorderView {
                view.removeFromSuperview()
            }
        }
        // Add new edges
        if edges.contains(.all) {
            addSidedBorder(toEdge: [.left,.right, .top, .bottom], withColor: color, inset: inset, thickness: thickness)
        }
        if edges.contains(.left) {
            addSidedBorder(toEdge: [.left], withColor: color, inset: inset, thickness: thickness)
        }
        if edges.contains(.right) {
            addSidedBorder(toEdge: [.right], withColor: color, inset: inset, thickness: thickness)
        }
        if edges.contains(.top) {
            addSidedBorder(toEdge: [.top], withColor: color, inset: inset, thickness: thickness)
        }
        if edges.contains(.bottom) {
            addSidedBorder(toEdge: [.bottom], withColor: color, inset: inset, thickness: thickness)
        }
    }

    private func addSidedBorder(toEdge edges: [RectangularEdges], withColor color: UIColor, inset: CGFloat = 0, thickness: CGFloat) {
        for edge in edges {
            let border = BorderView(frame: .zero)
            border.backgroundColor = color
            addSubview(border)
            border.translatesAutoresizingMaskIntoConstraints = false
            switch edge {
            case .left:
                NSLayoutConstraint.activate([
                border.leftAnchor.constraint(equalTo: self.leftAnchor, constant: inset),
                    border.topAnchor.constraint(equalTo: self.topAnchor, constant: inset),
                    border.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -inset),
                    NSLayoutConstraint(item: border, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: thickness) ])
            case .right:
                NSLayoutConstraint.activate([
                    border.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -inset),
                    border.topAnchor.constraint(equalTo: self.topAnchor, constant: inset),
                    border.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -inset),
                    NSLayoutConstraint(item: border, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: thickness) ])
            case .top:
                NSLayoutConstraint.activate([
                    border.leftAnchor.constraint(equalTo: self.leftAnchor, constant: inset),
                    border.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -inset),
                    border.topAnchor.constraint(equalTo: self.topAnchor, constant: inset),
                    NSLayoutConstraint(item: border, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: thickness) ])
            case .bottom:
                NSLayoutConstraint.activate([
                    border.leftAnchor.constraint(equalTo: self.leftAnchor, constant: inset),
                    border.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -inset),
                    border.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -inset),
                    NSLayoutConstraint(item: border, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: thickness) ])
            }
        }
    }

    private enum RectangularEdges {
        case left
        case right
        case top
        case bottom
    }
}
Andrej
fonte
1

Se eu estiver construindo a partir do storyboard, prefiro adicionar um UIViewpor trás do meu útil UIView... Se eu quiser criar uma borda na parte superior da minha UIView, apenas aumento a altura do plano UIViewde fundo pela largura da minha borda. O mesmo pode ser feito para qualquer outro lado :)

Adrián Rivero
fonte
0

Pessoalmente, gosto da subclassificação do view + drawRect, mas aqui está apenas outra maneira de fazer isso (e funciona da mesma forma que a resposta aceita por @ If Pollavith):

Sua nova camada de borda pode ser configurada para ter as dimensões desejadas. Então, como a resposta de @Se Pollavith, você cria uma camada para ser tão alta quanto quiser e tão larga quanto a vista que deseja delimitar. Use a definição de quadro da camada para colocá-lo onde quiser e, em seguida, adicione-o como uma subcamada à sua exibição.

Para referência, meu próprio requisito era colocar uma borda no lado esquerdo da vista (não recorte e cole esse código e me diga apenas porque não coloca uma borda no topo da vista - modificar o código abaixo é bastante simples):

    CALayer *leftBorder = [CALayer layer];
leftBorder.borderColor = [UIColor colorWithRed:0.0 green:91.0/255.0 blue:141.0/255.0 alpha:1.0].CGColor;
leftBorder.borderWidth = 1;
leftBorder.frame = CGRectMake(0, 0, 1.0, CGRectGetHeight(self.myTargetView.frame));
[self.myTargetView.layer addSublayer:leftBorder];

Eu acho que o único benefício moderado sobre isso e criar um pequeno UIView ou UILabel é que o CALayer é supostamente 'mais leve', e há muitas visões interessantes (como nas opiniões) sobre ultrapassar o drawRect versus usar o CALayers (como aqui : iOS: Usando o 'drawRect:' do UIView vs. o delagado de sua camada 'drawLayer: inContext:' ).

Animal451

Eu gosto da cor azul.

Animal451
fonte
0

Além do n8tr, pode-se acrescentar que existe uma disponibilidade para configurá-los no storyboard:
- adicione duas propriedades como borderColore borderWidthno arquivo .h;
- então você pode adicionar keyPathsno storyboard, veja o link para a captura de tela

Mykhailo Lysenko
fonte
0

Conversão DanShev para Swift 3

extension CALayer {

func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {

    let border = CALayer()

    switch edge {
    case .top:
        border.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: thickness)
        break
    case .bottom:
        border.frame = CGRect(x: 0, y: self.frame.height - thickness, width: self.frame.width, height: thickness)
        break
    case .left:
        border.frame = CGRect(x: 0, y: 0, width: thickness, height: self.frame.height)
        break
    case .right:
        border.frame = CGRect(x: self.frame.width - thickness, y: 0, width: thickness, height: self.frame.height)
        break
    default:
        break
    }

    border.backgroundColor = color.cgColor;

    self.addSublayer(border)
}
}
PUBLICIDADES
fonte
0

Para o Xamarin em C #, apenas crio a borda embutida ao adicionar a subcamada

  View.Layer.AddSublayer(new CALayer()
    {
        BackgroundColor = UIColor.Black.CGColor,
        Frame = new CGRect(0, 0, View.Frame.Width, 0.5f)
    });

Você pode organizar isso (como sugerido por outras pessoas) para as margens inferior, esquerda e direita.

Craig Champion
fonte
0

Você também pode verificar esta coleção das categorias UIKit e Foundation: https://github.com/leszek-s/LSCategories

Permite adicionar borda em um lado do UIView com uma única linha de código:

[self.someView lsAddBorderOnEdge:UIRectEdgeTop color:[UIColor blueColor] width:2];

e ele lida adequadamente com a rotação de visualizações, enquanto a maioria das respostas postadas aqui não é adequada.

Leszek Szary
fonte
0

Nota: a maioria das soluções aqui não é adaptável e não será redimensionada. As soluções que serão redimensionadas terão uma enorme impacto no tempo de inicialização, uma vez que usam muita CPU.

Você pode usar esta solução abaixo. Ele funciona em UIBezierPaths, que são mais leves que as camadas, causando tempos de inicialização rápidos. É fácil de usar, veja as instruções abaixo.

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. Defina a classe do seu UIView como ResizeBorderView
  2. Defina a cor e a largura da linha usando yourview.color e yourview.lineWidth no método viewDidAppear
  3. Defina as arestas, exemplo: yourview.edges = [.right, .left] ([.all]) para todos
  4. Aproveite o início rápido e o redimensionamento de bordas
J. Doe
fonte
0

Para definir a borda superior e a borda inferior para uma UIView no Swift.

let topBorder = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 1))
topBorder.backgroundColor = UIColor.black
myView.addSubview(topBorder)

let bottomBorder = UIView(frame: CGRect(x: 0, y: myView.frame.size.height - 1, width: 10, height: 1))
bottomBorder.backgroundColor = UIColor.black
myView.addSubview(bottomBorder)
Sudhi 9135
fonte
0

Nos Swift 4 e 3

let borderThickness = 2
let topBorder = UIView()
topBorder.backgroundColor = UIColor.red
topBorder.frame = CGRect(x: 0, y: 0, width: 
                  Int(yourViewFromOutlet.frame.size.width), height: 
                  borderThickness)
yourViewFromOutlet.addSubview(topBorder)
Naishta
fonte