¿Cómo dibujo una sombra debajo de una UIView?

UIView dibujar una sombra debajo del borde inferior de una UIView en Cocoa Touch. Entiendo que debería usar CGContextSetShadow() para dibujar la sombra, pero la guía de progtwigción 2D de Quartz es un poco vaga:

  1. Guarde el estado de los gráficos.
  2. Llame a la función CGContextSetShadow , pasando los valores apropiados.
  3. Realice todo el dibujo al que desee aplicar sombras.
  4. Restaurar el estado de los gráficos

Intenté lo siguiente en una subclase de UIView :

 - (void)drawRect:(CGRect)rect { CGContextRef currentContext = UIGraphicsGetCurrentContext(); CGContextSaveGState(currentContext); CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5); CGContextRestoreGState(currentContext); [super drawRect: rect]; } 

… pero esto no funciona para mí y estoy un poco atascado sobre (a) a dónde ir y (b) si hay algo que deba hacer a mi UIView para que esto funcione?

En tu código actual, guardas el GState del contexto actual, lo configuras para dibujar una sombra … y lo restauras a lo que era antes de que lo configuraras para dibujar una sombra. Entonces, finalmente, invocas la implementación de la drawRect de drawRect :.

Cualquier dibujo que debería verse afectado por la configuración de sombras debe suceder después

 CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5); 

pero antes

 CGContextRestoreGState(currentContext); 

Entonces, si quieres que el drawRect: la superclase drawRect: se “envuelva” en una sombra, ¿qué tal si reorganizas tu código de esta manera?

 - (void)drawRect:(CGRect)rect { CGContextRef currentContext = UIGraphicsGetCurrentContext(); CGContextSaveGState(currentContext); CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5); [super drawRect: rect]; CGContextRestoreGState(currentContext); } 

Un enfoque mucho más fácil es establecer algunos atributos de capa de la vista en la inicialización:

 self.layer.masksToBounds = NO; self.layer.shadowOffset = CGSizeMake(-15, 20); self.layer.shadowRadius = 5; self.layer.shadowOpacity = 0.5; 

Necesita importar QuartzCore.

 #import  
 self.layer.masksToBounds = NO; self.layer.cornerRadius = 8; // if you like rounded corners self.layer.shadowOffset = CGSizeMake(-15, 20); self.layer.shadowRadius = 5; self.layer.shadowOpacity = 0.5; 

Esto ralentizará la aplicación. Agregar la siguiente línea puede mejorar el rendimiento siempre que su vista sea visiblemente rectangular:

 self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath; 

La misma solución, pero solo para recordarle: puede definir la sombra directamente en el guión gráfico.

Ex:

enter image description here

Puedes probar esto … puedes jugar con los valores. El shadowRadius dicta la cantidad de desenfoque. shadowOffset dicta dónde va la sombra.

Swift 2.0

 let radius: CGFloat = demoView.frame.width / 2.0 //change it to .height if you need spread for height let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 2.1 * radius, height: demoView.frame.height)) //Change 2.1 to amount of spread you need and for height replace the code for height demoView.layer.cornerRadius = 2 demoView.layer.shadowColor = UIColor.blackColor().CGColor demoView.layer.shadowOffset = CGSize(width: 0.5, height: 0.4) //Here you control x and y demoView.layer.shadowOpacity = 0.5 demoView.layer.shadowRadius = 5.0 //Here your control your blur demoView.layer.masksToBounds = false demoView.layer.shadowPath = shadowPath.CGPath 

Swift 3.0

 let radius: CGFloat = demoView.frame.width / 2.0 //change it to .height if you need spread for height let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 2.1 * radius, height: demoView.frame.height)) //Change 2.1 to amount of spread you need and for height replace the code for height demoView.layer.cornerRadius = 2 demoView.layer.shadowColor = UIColor.black.cgColor demoView.layer.shadowOffset = CGSize(width: 0.5, height: 0.4) //Here you control x and y demoView.layer.shadowOpacity = 0.5 demoView.layer.shadowRadius = 5.0 //Here your control your blur demoView.layer.masksToBounds = false demoView.layer.shadowPath = shadowPath.cgPath 

Ejemplo con spread

Ejemplo con spread

Para crear una sombra básica

  demoView.layer.cornerRadius = 2 demoView.layer.shadowColor = UIColor.blackColor().CGColor demoView.layer.shadowOffset = CGSizeMake(0.5, 4.0); //Here your control your spread demoView.layer.shadowOpacity = 0.5 demoView.layer.shadowRadius = 5.0 //Here your control your blur 

Ejemplo básico de sombras en Swift 2.0

SALIDA

Solución simple y limpia usando Interface Builder

Agregue un archivo llamado UIView.swift en su proyecto (o simplemente péguelo en cualquier archivo):

 import UIKit @IBDesignable extension UIView { /* The color of the shadow. Defaults to opaque black. Colors created * from patterns are currently NOT supported. Animatable. */ @IBInspectable var shadowColor: UIColor? { set { layer.shadowColor = newValue!.CGColor } get { if let color = layer.shadowColor { return UIColor(CGColor:color) } else { return nil } } } /* The opacity of the shadow. Defaults to 0. Specifying a value outside the * [0,1] range will give undefined results. Animatable. */ @IBInspectable var shadowOpacity: Float { set { layer.shadowOpacity = newValue } get { return layer.shadowOpacity } } /* The shadow offset. Defaults to (0, -3). Animatable. */ @IBInspectable var shadowOffset: CGPoint { set { layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y) } get { return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height) } } /* The blur radius used to create the shadow. Defaults to 3. Animatable. */ @IBInspectable var shadowRadius: CGFloat { set { layer.shadowRadius = newValue } get { return layer.shadowRadius } } } 

Luego, estará disponible en el Creador de interfaces para cada vista en el Panel de utilidades> Inspector de atributos:

Panel de utilidades

Puedes configurar fácilmente la sombra ahora.

Notas:
– La sombra no aparecerá en IB, solo en tiempo de ejecución.
– Como dijo Mazen Kasser

Para aquellos que no lograron que esto funcione, […] asegúrese de que Clip Subviews ( clipsToBounds ) no esté habilitado.

Yo uso esto como parte de mis utilidades. Con esto no solo podemos establecer sombras sino que también podemos obtener una esquina redondeada para cualquier UIView . También puede establecer qué sombra de color prefiere. Normalmente se prefiere el negro, pero a veces, cuando el fondo no es blanco, es posible que desee algo más. Esto es lo que uso:

 in utils.m + (void)roundedLayer:(CALayer *)viewLayer radius:(float)r shadow:(BOOL)s { [viewLayer setMasksToBounds:YES]; [viewLayer setCornerRadius:r]; [viewLayer setBorderColor:[RGB(180, 180, 180) CGColor]]; [viewLayer setBorderWidth:1.0f]; if(s) { [viewLayer setShadowColor:[RGB(0, 0, 0) CGColor]]; [viewLayer setShadowOffset:CGSizeMake(0, 0)]; [viewLayer setShadowOpacity:1]; [viewLayer setShadowRadius:2.0]; } return; } 

Para utilizar esto necesitamos llamar a esto – [utils roundedLayer:yourview.layer radius:5.0f shadow:YES];

Swift 3

 extension UIView { func installShadow() { layer.cornerRadius = 2 layer.masksToBounds = false layer.shadowColor = UIColor.black.cgColor layer.shadowOffset = CGSize(width: 0, height: 1) layer.shadowOpacity = 0.45 layer.shadowPath = UIBezierPath(rect: bounds).cgPath layer.shadowRadius = 1.0 } } 

Para aquellos que no lograron hacer que esto funcione (¡como yo mismo!) Después de probar todas las respuestas aquí, simplemente asegúrense de que las Subvistas de Clip no estén habilitadas en el inspector de Atributos …

Puede usar mi función de utilidad creada para el radio de sombra y esquina de la siguiente manera:

 - (void)addShadowWithRadius:(CGFloat)shadowRadius withShadowOpacity:(CGFloat)shadowOpacity withShadowOffset:(CGSize)shadowOffset withShadowColor:(UIColor *)shadowColor withCornerRadius:(CGFloat)cornerRadius withBorderColor:(UIColor *)borderColor withBorderWidth:(CGFloat)borderWidth forView:(UIView *)view{ // drop shadow [view.layer setShadowRadius:shadowRadius]; [view.layer setShadowOpacity:shadowOpacity]; [view.layer setShadowOffset:shadowOffset]; [view.layer setShadowColor:shadowColor.CGColor]; // border radius [view.layer setCornerRadius:cornerRadius]; // border [view.layer setBorderColor:borderColor.CGColor]; [view.layer setBorderWidth:borderWidth]; } 

¡¡¡Espero que te ayude!!!

Todos Responden bien pero quiero agregar un punto más

Si encuentra un problema cuando tiene celdas de tabla, Deque una nueva celda hay una discrepancia en la sombra así que en este caso, necesita colocar su código de sombra en un método de layoutSubviews para que se comporte bien en todas las condiciones.

 -(void)layoutSubviews{ [super layoutSubviews]; [self.contentView setNeedsLayout]; [self.contentView layoutIfNeeded]; [VPShadow applyShadowView:self]; } 

o en ViewControllers para una vista específica coloque el código de sombra dentro del siguiente método para que funcione bien

 -(void)viewDidLayoutSubviews{ [super viewDidLayoutSubviews]; [self.viewShadow layoutIfNeeded]; [VPShadow applyShadowView:self.viewShadow]; } 

He modificado mi implementación de shadow para nuevos desarrolladores para una forma más general ex:

 /*! @brief Add shadow to a view. @param layer CALayer of the view. */ +(void)applyShadowOnView:(CALayer *)layer OffsetX:(CGFloat)x OffsetY:(CGFloat)y blur:(CGFloat)radius opacity:(CGFloat)alpha RoundingCorners:(CGFloat)cornerRadius{ UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:layer.bounds cornerRadius:cornerRadius]; layer.masksToBounds = NO; layer.shadowColor = [UIColor blackColor].CGColor; layer.shadowOffset = CGSizeMake(x,y);// shadow x and y layer.shadowOpacity = alpha; layer.shadowRadius = radius;// blur effect layer.shadowPath = shadowPath.CGPath; } 

Swift 3

 self.paddingView.layer.masksToBounds = false self.paddingView.layer.shadowOffset = CGSize(width: -15, height: 10) self.paddingView.layer.shadowRadius = 5 self.paddingView.layer.shadowOpacity = 0.5 

Si desea utilizar StoryBoard y no desea seguir escribiendo en los atributos de tiempo de ejecución, puede crear fácilmente una extensión para las vistas y hacer que se puedan usar en el guión gráfico.

Paso 1. crear extensión

 extension UIView { @IBInspectable var shadowRadius: CGFloat { get { return layer.shadowRadius } set { layer.shadowRadius = newValue } } @IBInspectable var shadowOpacity: Float { get { return layer.shadowOpacity } set { layer.shadowOpacity = newValue } } @IBInspectable var shadowOffset: CGSize { get { return layer.shadowOffset } set { layer.shadowOffset = newValue } } @IBInspectable var maskToBound: Bool { get { return layer.masksToBounds } set { layer.masksToBounds = newValue } } } 

paso 2. ahora puedes usar estos atributos en el guión gráfico imagen del guión gráfico