¿Cuál es la mejor manera de agregar una sombra paralela a mi UIView?

Estoy tratando de agregar una sombra paralela a las vistas que están superpuestas una encima de la otra, las vistas se colapsan permitiendo que se vea el contenido de otras vistas, en este sentido quiero mantener view.clipsToBounds ENCENDIDO para que cuando las vistas colapsen, su contenido quede recortado.

Esto parece haberme dificultado agregar una sombra paralela a las capas como cuando giro clipsToBounds ON las sombras también se recortan.

He estado tratando de manipular view.frame y view.bounds para agregar una sombra paralela al marco pero permitir que los límites sean lo suficientemente grandes como para abarcarlo, sin embargo, no he tenido suerte con esto.

Aquí está el código que estoy usando para agregar una sombra (esto solo funciona con clipsToBounds APAGADO como se muestra)

view.clipsToBounds = NO;
view.layer.shadowColor = [[UIColor blackColor] CGColor];
view.layer.shadowOffset = CGSizeMake(0,5);
view.layer.shadowOpacity = 0.5;

Aquí hay una captura de pantalla de la sombra que se aplica a la capa gris más clara superior. Con suerte, esto da una idea de cómo se superpondrá mi contenido si clipsToBounds esta apagado.

Aplicación de sombra.

¿Cómo puedo agregar una sombra a mi UIView y mantener mi contenido recortado?

Editar: Solo quería agregar que también he jugado con el uso de imágenes de fondo con sombras, lo que funciona bien, sin embargo, aún me gustaría saber cuál es la mejor solución codificada para esto.

preguntado el 18 de marzo de 12 a las 18:03

7 Respuestas

Prueba esto:

UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:view.bounds];
view.layer.masksToBounds = NO;
view.layer.shadowColor = [UIColor blackColor].CGColor;
view.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = shadowPath.CGPath;

Ante todo: Los UIBezierPath Usado como shadowPath Es crucial. Si no lo usa, es posible que no note una diferencia al principio, pero el ojo atento observará que se produce un cierto retraso durante eventos como girar el dispositivo y / o similares. Es un cambio de rendimiento importante.

Con respecto a su problema específicamente: La línea importante es view.layer.masksToBounds = NO. Desactiva el recorte de las subcapas de la capa de la vista que se extienden más allá de los límites de la vista.

Para aquellos que se preguntan cuál es la diferencia entre masksToBounds (en la capa) y la propia vista clipToBounds La propiedad es: Realmente no hay ninguna. Alternar uno tendrá un efecto sobre el otro. Solo un nivel diferente de abstracción.


Rápido 2.2:

override func layoutSubviews()
{
    super.layoutSubviews()

    let shadowPath = UIBezierPath(rect: bounds)
    layer.masksToBounds = false
    layer.shadowColor = UIColor.blackColor().CGColor
    layer.shadowOffset = CGSizeMake(0.0, 5.0)
    layer.shadowOpacity = 0.5
    layer.shadowPath = shadowPath.CGPath
}

Rápido 3:

override func layoutSubviews()
{
    super.layoutSubviews()

    let shadowPath = UIBezierPath(rect: bounds)
    layer.masksToBounds = false
    layer.shadowColor = UIColor.black.cgColor
    layer.shadowOffset = CGSize(width: 0.0, height: 5.0)
    layer.shadowOpacity = 0.5
    layer.shadowPath = shadowPath.cgPath
}

Respondido 07 Oct 16, 15:10

Gracias, probé tu código y luego intenté agregar masksToBounds = NO; a mi original - con ambos intentos mantuve clipsToBounds = YES; ACTIVADO: ninguno de los dos pudo recortar el contenido. aquí hay una captura de pantalla de lo que sucedió con su ejemplo> youtu.be/tdpemc_Xdps - wez

¿Alguna idea sobre cómo hacer que la sombra paralela abrace una esquina redondeada en lugar de renderizar como si la esquina fuera todavía un cuadrado? - Juan Erck

@JohnErck prueba esto: UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds cornerRadius:5.0];. No probado, pero debería producir el resultado que desea. - pkluz

He aprendido que al animar la vista, la sombra deja huellas grises mientras se mueve. Eliminar las líneas shadowPath resolvió esto: ishahak

@shoe porque cada vez que cambia el tamaño de la vista, layoutSubviews() se llama. Y si no compensamos en ese lugar ajustando el shadowPath para reflejar el tamaño actual, tendríamos una vista redimensionada, pero la sombra seguiría siendo el tamaño anterior (inicial) y no se mostraría o se asomó donde no debería. - pkluz

La respuesta de Wasabii en Swift 2.3:

let shadowPath = UIBezierPath(rect: view.bounds)
view.layer.masksToBounds = false
view.layer.shadowColor = UIColor.blackColor().CGColor
view.layer.shadowOffset = CGSize(width: 0, height: 0.5)
view.layer.shadowOpacity = 0.2
view.layer.shadowPath = shadowPath.CGPath

Y en Swift 3/4/5:

let shadowPath = UIBezierPath(rect: view.bounds)
view.layer.masksToBounds = false
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOffset = CGSize(width: 0, height: 0.5)
view.layer.shadowOpacity = 0.2
view.layer.shadowPath = shadowPath.cgPath

Coloque este código en layoutSubviews () si está utilizando AutoLayout.

En SwiftUI, todo esto es mucho más fácil:

Color.yellow  // or whatever your view
    .shadow(radius: 3)
    .frame(width: 200, height: 100)

Respondido 05 Oct 19, 13:10

Gracias por notar layoutSubviews - Islam

o incluso mejor en viewDidLayoutSubviews () - Max MacLeod

prefieren inicializadores de estructura rápidos en lugar de variantes de Make, es decir CGSize(width: 0, height: 0.5) - Oliver Atkinson

Agregué este código a layoutSubviews (), todavía no se procesó en IB. ¿Hay otras configuraciones en las que deba participar? - burro

Gracias. Estaba usando Auto Layout y pensé para mí mismo, bueno ... view.bounds no ha sido creado tan obviamente que shadowPath no se puede hacer referencia a la vista recta, pero algunos CGRectZero en lugar de. De todos modos, poniendo esto debajo layoutSubviews resolvió mi problema por fin! - desarrollador

El truco es definir el masksToBounds propiedad de la capa de su vista correctamente:

view.layer.masksToBounds = NO;

y debería funcionar.

(Fuente)

Respondido 01 Feb 16, 11:02

Puede crear una extensión para que UIView acceda a estos valores en el editor de diseño

Opciones de sombra en el editor de diseño

extension UIView{

    @IBInspectable var shadowOffset: CGSize{
        get{
            return self.layer.shadowOffset
        }
        set{
            self.layer.shadowOffset = newValue
        }
    }

    @IBInspectable var shadowColor: UIColor{
        get{
            return UIColor(cgColor: self.layer.shadowColor!)
        }
        set{
            self.layer.shadowColor = newValue.cgColor
        }
    }

    @IBInspectable var shadowRadius: CGFloat{
        get{
            return self.layer.shadowRadius
        }
        set{
            self.layer.shadowRadius = newValue
        }
    }

    @IBInspectable var shadowOpacity: Float{
        get{
            return self.layer.shadowOpacity
        }
        set{
            self.layer.shadowOpacity = newValue
        }
    }
}

Respondido 06 Abr '17, 16:04

cómo hacer referencia a esa extensión en el generador de interfaces - Amr enojado

IBInspectable lo hace disponible en el constructor de interfaces - nitesh

¿Puedo lograr esto también en Objective C? - Harish Pathak

Si desea tener una vista previa en tiempo real (en el generador de interfaces), aquí puede encontrar un buen artículo al respecto. spin.atomicobject.com/2017/07/18/swift-interface-builder - habilidad médica

También puede configurar la sombra a su vista desde el guión gráfico

enter image description here

respondido 22 mar '18, 07:03

A la vistaWillLayoutSubviews:

override func viewWillLayoutSubviews() {
    sampleView.layer.masksToBounds =  false
    sampleView.layer.shadowColor = UIColor.darkGrayColor().CGColor;
    sampleView.layer.shadowOffset = CGSizeMake(2.0, 2.0)
    sampleView.layer.shadowOpacity = 1.0
}

Usando la extensión de UIView:

extension UIView {

    func addDropShadowToView(targetView:UIView? ){
        targetView!.layer.masksToBounds =  false
        targetView!.layer.shadowColor = UIColor.darkGrayColor().CGColor;
        targetView!.layer.shadowOffset = CGSizeMake(2.0, 2.0)
        targetView!.layer.shadowOpacity = 1.0
    }
}

Uso:

sampleView.addDropShadowToView(sampleView)

contestado el 10 de mayo de 16 a las 12:05

Solo hacer cumplir targetView: UIView y quitar el flequillo. - azulbambú

Por qué no usar self? Me parece mucho más conveniente. - LinusGeffarth

Una mejor manera de hacer esto es eliminar targetView como parámetro y usar self en lugar de targetView. Esto elimina la redundancia y permite llamarlo así: sampleView.addDropShadowToView () - maxcodes

Los comentarios de Max Nelson son geniales. Mi sugerencia es usar if let sintaxis en lugar de ! - Johnny

Entonces, sí, debería preferir la propiedad shadowPath para el rendimiento, pero también: Desde el archivo de encabezado de CALayer.shadowPath

Especificar la ruta explícitamente usando esta propiedad generalmente * mejorará el rendimiento de la representación, al igual que compartir la misma ruta * referencia en múltiples capas

Un truco menos conocido es compartir la misma referencia en varias capas. Por supuesto, tienen que usar la misma forma, pero esto es común con las celdas de vista de tabla / colección.

No sé por qué se vuelve más rápido si comparte instancias, supongo que almacena en caché la representación de la sombra y puede reutilizarla para otras instancias en la vista. Me pregunto si esto es incluso más rápido con

contestado el 04 de mayo de 19 a las 00:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.