Agregue un borde fuera de una UIView (en lugar de dentro)

82

Si agrega un borde de una vista usando código en una vista como

self.layer.borderColor = [UIColor yellowColor].CGColor;
self.layer.borderWidth = 2.0f;

el borde se agrega dentro de la vista de la siguiente manera: ingrese la descripción de la imagen aquí

la vista de la derecha es la vista original, como puede ver, el área negra de la vista bordeada es menor que la original. pero lo que quiero llegar es una frontera fuera de vista original, como esto: ingrese la descripción de la imagen aquí. el área negra es igual al original, ¿cómo puedo implementarlo?

remykits
fuente

Respuestas:

100

Desafortunadamente, no hay simplemente una pequeña propiedad que pueda configurar para alinear el borde con el exterior. Se dibuja alineado hacia el interior porque las operaciones de dibujo predeterminadas de UIViews se dibujan dentro de sus límites.

La solución más simple que me viene a la mente sería expandir la UIView por el tamaño del ancho del borde al aplicar el borde:

CGFloat borderWidth = 2.0f;

self.frame = CGRectInset(self.frame, -borderWidth, -borderWidth);
self.layer.borderColor = [UIColor yellowColor].CGColor;
self.layer.borderWidth = borderWidth;
Elliott James Perry
fuente
He usado esto en la vista de uitableviewcell. pero en la línea, self.frame = CGRectInset (self.frame, -borderWidth, -borderWidth); El ancho y la altura de la vista siguen aumentando si seguimos desplazando la vista de la tabla. Así que terminé eliminándola.
Neela
Este método sigue aumentando la altura de la vista, así que elimino este código y uso el siguiente código self.view.layer.borderColor = [[UIColor colorWithRed: 209.0f / 255.0f green: 33.0f / 255.0f blue: 8.0f / 255.0f alpha: 1.0f] CGColor]; self.view.layer.borderWidth = 1.0f;
Rizwan Shah
4
Probé esta solución en Swift, desafortunadamente no funciona, el borde sigue dibujándose dentro de UIImageView
theDC
Si este código está dentro de un método que se llama varias veces, seguirá aumentando de tamaño porque self.frame se reitera. Para evitar esto, declare una propiedad para almacenar self.frame y configúrelo en viewDidload. por ejemplo, _originalSize = self.frame; donde originalSize es una propiedad CGRect. Luego cambie el código a self.frame = CGRectInset (_originalSize, -borderWidth, -borderWidth);
GeneCode
Cuando esto se implementa en ViewDidLoad, la vista cambia automáticamente de tamaño al tamaño de la pantalla (es decir, la vista cambia al tamaño original y, por lo tanto, el borde se vuelve visible). Si se implementa en ViewDidAppear, no crea un borde fuera de la vista original. ¿Alguna idea sobre cómo implementarlo para que el borde se cree fuera de la vista original Y la vista no cambie de tamaño?
JeffB6688
23

Ok, ya hay una respuesta aceptada, pero creo que hay una mejor manera de hacerlo, solo tienes que tener una nueva capa un poco más grande que tu vista y no enmascararla a los límites de la capa de la vista (que en realidad es el comportamiento predeterminado). Aquí está el código de ejemplo :

CALayer * externalBorder = [CALayer layer];
externalBorder.frame = CGRectMake(-1, -1, myView.frame.size.width+2, myView.frame.size.height+2);
externalBorder.borderColor = [UIColor blackColor].CGColor;
externalBorder.borderWidth = 1.0;

[myView.layer addSublayer:externalBorder];
myView.layer.masksToBounds = NO;

Por supuesto, esto es si desea que su borde sea 1 unidad más grande, si desea más, adapte el borderWidthy el marco de la capa en consecuencia. Esto es mejor que usar una segunda vista un poco más grande ya que a CALayeres más ligero que a UIViewy no tienes que modificar el marco de myView, lo cual es bueno, por ejemplo, si myViewes unUIImageView

NB: Para mí, el resultado no fue perfecto en el simulador (la capa no estaba exactamente en la posición correcta, por lo que la capa era más gruesa en un lado a veces) pero fue exactamente lo que se pidió en un dispositivo real.

EDITAR

En realidad el problema del que hablo en el NB fue solo porque había reducido la pantalla del simulador, en tamaño normal no hay absolutamente ningún problema

Espero eso ayude

Hugues Duvillier
fuente
2
myView.layer.masksToBounds = NO; está creando un problema cuando myView tiene CornerRadius.
umakanta
.masksToBounds = no // esto también desbordará su imagen si el modo de escalado es aspectFill
iOS Blacksmith
22

Con la mejor respuesta aceptada anteriormente, hice experiencias con resultados no agradables y bordes antiestéticos:

borde sin camino bezier

Así que compartiré mi extensión UIView Swift con usted, que usa un UIBezierPath en su lugar como contorno de borde, sin bordes antiestéticos (inspirado en @Fattie ):

borde con camino bezier

//  UIView+BezierPathBorder.swift

import UIKit

extension UIView {

    fileprivate var bezierPathIdentifier:String { return "bezierPathBorderLayer" }

    fileprivate var bezierPathBorder:CAShapeLayer? {
        return (self.layer.sublayers?.filter({ (layer) -> Bool in
            return layer.name == self.bezierPathIdentifier && (layer as? CAShapeLayer) != nil
        }) as? [CAShapeLayer])?.first
    }

    func bezierPathBorder(_ color:UIColor = .white, width:CGFloat = 1) {

        var border = self.bezierPathBorder
        let path = UIBezierPath(roundedRect: self.bounds, cornerRadius:self.layer.cornerRadius)
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        self.layer.mask = mask

        if (border == nil) {
            border = CAShapeLayer()
            border!.name = self.bezierPathIdentifier
            self.layer.addSublayer(border!)
        }

        border!.frame = self.bounds
        let pathUsingCorrectInsetIfAny =
            UIBezierPath(roundedRect: border!.bounds, cornerRadius:self.layer.cornerRadius)

        border!.path = pathUsingCorrectInsetIfAny.cgPath
        border!.fillColor = UIColor.clear.cgColor
        border!.strokeColor = color.cgColor
        border!.lineWidth = width * 2
    }

    func removeBezierPathBorder() {
        self.layer.mask = nil
        self.bezierPathBorder?.removeFromSuperlayer()
    }

}

Ejemplo:

let view = UIView(frame: CGRect(x: 20, y: 20, width: 100, height: 100))
view.layer.cornerRadius = view.frame.width / 2
view.backgroundColor = .red

//add white 2 pixel border outline
view.bezierPathBorder(.white, width: 2)

//remove border outline (optional)
view.removeBezierPathBorder()
Peter Kreinz
fuente
en mi caso debería estar adentro :)
Peter Kreinz
buena forma de pensar, pero su borde todavía está "dentro", por lo que debe arreglarse para que sea delineado :)
Serj Rubens
15

Para una implementación de Swift, puede agregar esto como una extensión de UIView.

extension UIView {

    struct Constants {
        static let ExternalBorderName = "externalBorder"
    }

    func addExternalBorder(borderWidth: CGFloat = 2.0, borderColor: UIColor = UIColor.whiteColor()) -> CALayer {
        let externalBorder = CALayer()
        externalBorder.frame = CGRectMake(-borderWidth, -borderWidth, frame.size.width + 2 * borderWidth, frame.size.height + 2 * borderWidth)
        externalBorder.borderColor = borderColor.CGColor
        externalBorder.borderWidth = borderWidth
        externalBorder.name = Constants.ExternalBorderName

        layer.insertSublayer(externalBorder, atIndex: 0)
        layer.masksToBounds = false

        return externalBorder
    }

    func removeExternalBorders() {
        layer.sublayers?.filter() { $0.name == Constants.ExternalBorderName }.forEach() {
            $0.removeFromSuperlayer()
        }
    }

    func removeExternalBorder(externalBorder: CALayer) {
        guard externalBorder.name == Constants.ExternalBorderName else { return }
        externalBorder.removeFromSuperlayer()
    }

}
picciano
fuente
¡Excelente! ¡Gracias! Algunas cosas han cambiado en Swift 4 y ahora se ven diferentes, pero Xcode explica cómo modificar el código. Y en el método 'removeExternalBorder' debe ser 'guard externalBorder.name =='.
DmitryKanunnikoff
13

Bueno, no hay un método directo para hacerlo. Puede considerar algunas soluciones.

  1. Cambie y aumente el marco y agregue el color del borde como lo hizo
  2. Agregue una vista detrás de la vista actual con el tamaño más grande para que aparezca como borde. Se puede trabajar como una clase de vista personalizada.
  3. Si no necesita un borde definido (borde claro), puede depender de la sombra para el propósito

    [view1 setBackgroundColor:[UIColor blackColor]];
    UIColor *color = [UIColor yellowColor];
    view1.layer.shadowColor = [color CGColor];
    view1.layer.shadowRadius = 10.0f;
    view1.layer.shadowOpacity = 1;
    view1.layer.shadowOffset = CGSizeZero;
    view1.layer.masksToBounds = NO;
    
Lithu TV
fuente
2

Aumente el ancho y el alto del marco de la vista con el ancho del borde antes de agregar el borde:

float borderWidth = 2.0f
CGRect frame = self.frame;
frame.width += borderWidth;
frame.height += borderWidth;
 self.layer.borderColor = [UIColor yellowColor].CGColor;
 self.layer.borderWidth = 2.0f;
Hossam Ghareeb
fuente
2

En realidad, existe una solución muy sencilla. Simplemente configúrelos de esta manera:

view.layer.borderWidth = 5

view.layer.borderColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.5).cgColor

view.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.25).cgColor

fuente
1

Me gustó la solución de @picciano Si desea un círculo explosivo en lugar de un cuadrado, reemplace la función addExternalBorder con:

func addExternalBorder(borderWidth: CGFloat = 2.0, borderColor: UIColor = UIColor.white) {
        let externalBorder = CALayer()
        externalBorder.frame = CGRect(x: -borderWidth, y: -borderWidth, width: frame.size.width + 2 * borderWidth, height: frame.size.height + 2 * borderWidth)
        externalBorder.borderColor = borderColor.cgColor
        externalBorder.borderWidth = borderWidth
        externalBorder.cornerRadius = (frame.size.width + 2 * borderWidth) / 2
        externalBorder.name = Constants.ExternalBorderName
        layer.insertSublayer(externalBorder, at: 0)
        layer.masksToBounds = false

    }
Maksim Kniazev
fuente
0

Me gustó la solución de @picciano y @Maksim Kniazev. También podemos crear un borde anular con lo siguiente:

func addExternalAnnularBorder(borderWidth: CGFloat = 2.0, borderColor: UIColor = UIColor.white) {
    let externalBorder = CALayer()
    externalBorder.frame = CGRect(x: -borderWidth*2, y: -borderWidth*2, width: frame.size.width + 4 * borderWidth, height: frame.size.height + 4 * borderWidth)
    externalBorder.borderColor = borderColor.cgColor
    externalBorder.borderWidth = borderWidth
    externalBorder.cornerRadius = (frame.size.width + 4 * borderWidth) / 2
    externalBorder.name = Constants.ExternalBorderName
    layer.insertSublayer(externalBorder, at: 0)
    layer.masksToBounds = false
}
Jimbung
fuente
0

La forma en que coloqué un borde alrededor de mi vista de IU (principal - SubscriptionAd) en Storyboard es colocarlo dentro de otra vista de IU (background - BackgroundAd). La UIView de fondo tiene un color de fondo que coincide con el color del borde que quiero, y la UIView principal tiene un valor de restricciones 2 en cada lado.

Vincularé la vista de fondo a mi ViewController y luego encenderé y apagaré el borde cambiando el color de fondo.

Imagen de UIView anidada con restricciones de 2px de vista superior alrededor, haciéndola más pequeña que grande

JonesJr876
fuente
0

Rápido 5

extension UIView {
    fileprivate struct Constants {
        static let externalBorderName = "externalBorder"
    }

    func addExternalBorder(borderWidth: CGFloat = 2.0, borderColor: UIColor = UIColor.white) -> CALayer {
        let externalBorder = CALayer()
        externalBorder.frame = CGRect(x: -borderWidth, y: -borderWidth, width: frame.size.width + 2 * borderWidth, height: frame.size.height + 2 * borderWidth)
        externalBorder.borderColor = borderColor.cgColor
        externalBorder.borderWidth = borderWidth
        externalBorder.name = Constants.ExternalBorderName

        layer.insertSublayer(externalBorder, at: 0)
        layer.masksToBounds = false

        return externalBorder
    }

    func removeExternalBorders() {
        layer.sublayers?.filter() { $0.name == Constants.externalBorderName }.forEach() {
            $0.removeFromSuperlayer()
        }
    }

    func removeExternalBorder(externalBorder: CALayer) {
        guard externalBorder.name == Constants.externalBorderName else { return }
        externalBorder.removeFromSuperlayer()
    }
}
Pacyjent
fuente