UIView con esquinas redondeadas y sombra paralela

Literalmente he intentado todo lo que pude, pero ninguno de ellos funciona para una UIView personalizada … Solo quería una vista blanca en blanco con esquinas redondeadas y una ligera sombra (sin efecto de iluminación). Puedo hacer cada uno de esos uno por uno, pero se producen los conflictos habituales de clipToBounds / maskToBounds .

El siguiente fragmento de código agrega un borde, un radio de borde y una sombra UIView a v , un UIView :

 // border radius [v.layer setCornerRadius:30.0f]; // border [v.layer setBorderColor:[UIColor lightGrayColor].CGColor]; [v.layer setBorderWidth:1.5f]; // drop shadow [v.layer setShadowColor:[UIColor blackColor].CGColor]; [v.layer setShadowOpacity:0.8]; [v.layer setShadowRadius:3.0]; [v.layer setShadowOffset:CGSizeMake(2.0, 2.0)]; 

Puede ajustar la configuración para satisfacer sus necesidades.

Además, agregue el marco QuartzCore a su proyecto y:

 #import  

Ver mi otra respuesta con respecto a masksToBounds .


Nota

Esto puede no funcionar en todos los casos. Si encuentra que este método interfiere con otras operaciones de dibujo que está realizando, consulte esta respuesta .

Rápido

enter image description here

 // corner radius blueView.layer.cornerRadius = 10 // border blueView.layer.borderWidth = 1.0 blueView.layer.borderColor = UIColor.black.cgColor // shadow blueView.layer.shadowColor = UIColor.black.cgColor blueView.layer.shadowOffset = CGSize(width: 3, height: 3) blueView.layer.shadowOpacity = 0.7 blueView.layer.shadowRadius = 4.0 

Explorando las opciones

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

Problema 1: Shadow es recortado

¿Qué pasa si hay subcapas o subvistas (como una imagen) cuyo contenido queremos recortar a los límites de nuestra vista?

enter image description here

Podemos lograr esto con

 blueView.layer.masksToBounds = true 

(Alternativamente, blueView.clipsToBounds = true da el mismo resultado ).

enter image description here

Pero, oh no! ¡La sombra también fue recortada porque está fuera de los límites! ¿Qué hacer? ¿Qué hacer?

Solución

Use vistas separadas para la sombra y el borde. La vista base es transparente y tiene la sombra. La vista de borde recorta cualquier otro subcontenido que tenga a sus bordes.

 // add the shadow to the base view baseView.backgroundColor = UIColor.clear baseView.layer.shadowColor = UIColor.black.cgColor baseView.layer.shadowOffset = CGSize(width: 3, height: 3) baseView.layer.shadowOpacity = 0.7 baseView.layer.shadowRadius = 4.0 // add the border to subview let borderView = UIView() borderView.frame = baseView.bounds borderView.layer.cornerRadius = 10 borderView.layer.borderColor = UIColor.black.cgColor borderView.layer.borderWidth = 1.0 borderView.layer.masksToBounds = true baseView.addSubview(borderView) // add any other subcontent that you want clipped let otherSubContent = UIImageView() otherSubContent.image = UIImage(named: "lion") otherSubContent.frame = borderView.bounds borderView.addSubview(otherSubContent) 

Esto da el siguiente resultado:

enter image description here

Problema 2: bajo rendimiento

Agregar esquinas redondeadas y sombras puede ser un golpe de rendimiento. Puede mejorar el rendimiento utilizando una ruta predefinida para la sombra y también especificando que se rasterice. El siguiente código se puede agregar al ejemplo anterior.

 baseView.layer.shadowPath = UIBezierPath(roundedRect: baseView.bounds, cornerRadius: 10).cgPath baseView.layer.shouldRasterize = true baseView.layer.rasterizationScale = UIScreen.main.scale 

Vea esta publicación para más detalles. Vea aquí y aquí también.

Esta respuesta fue probada con Swift 4 y Xcode 9.

Una forma de hacerlo es colocar la vista con esquinas redondeadas en una vista con la sombra paralela.

 UIView* roundedView = [[UIView alloc] initWithFrame: frame]; roundedView.layer.cornerRadius = 5.0; roundedView.layer.masksToBounds = YES; UIView* shadowView = [[UIView alloc] initWithFrame: frame]; shadowView.layer.shadowColor = [UIColor blackColor].CGColor; shadowView.layer.shadowRadius = 5.0; shadowView.layer.shadowOffset = CGSizeMake(3.0, 3.0); shadowView.layer.shadowOpacity = 1.0; [shadowView addSubview: roundedView]; 

Luego puede agregar shadowView donde desee.

Esto funcionó para mí. El truco fue mover el color de fondo de la vista principal a la capa.

 CALayer *layer = view.layer; layer.cornerRadius = 15.0f; layer.masksToBounds = NO; layer.shadowOffset = CGSizeMake(0, 3); layer.shadowColor = [[UIColor blackColor] CGColor]; layer.shadowRadius = 2.0f; layer.shadowOpacity = 0.35f; layer.shadowPath = [[UIBezierPath bezierPathWithRoundedRect:layer.bounds cornerRadius:layer.cornerRadius] CGPath]; CGColorRef bColor = view.backgroundColor.CGColor; view.backgroundColor = nil; layer.backgroundColor = bColor ; 

Solución simple de Swift 3.2 / 4 basada en la respuesta de Ade sin subvistas ni subtítulos adicionales:

 extension UIView { func addShadow(offset: CGSize, color: UIColor, radius: CGFloat, opacity: Float) { layer.masksToBounds = false layer.shadowOffset = offset layer.shadowColor = color.cgColor layer.shadowRadius = radius layer.shadowOpacity = opacity let backgroundCGColor = backgroundColor?.cgColor backgroundColor = nil layer.backgroundColor = backgroundCGColor } } 

Tenga en cuenta que debe configurar su vista con el radio de la esquina y otras propiedades antes de llamar a addShadow .

La llamada esto directamente desde viewDidLoad esta manera:

 btnBottom.addShadow(offset: CGSize.init(width: 0, height: 3), color: UIColor.black, radius: 2.0, opacity: 0.35) 

Resultado final:

resultado

Súper fácil y simple!

Resolví el problema usando el siguiente truco al asignar la ruta de sombra para la vista de contenedor:

 [UIBezierPath bezierPathWithRoundedRect:cell.bounds cornerRadius:12] 

Observe que la ruta dada a la sombra es un rectángulo redondeado con el mismo radio de esquina que el fondo que contiene la celda:

 //this is the border for the UIView that is added to a cell cell.backgroundView.layer.cornerRadius = 12; cell.backgroundView.layer.masksToBounds = YES; cell.backgroundView.layer.borderColor = [UIColor darkGrayColor].CGColor; cell.backgroundView.layer.borderWidth = 1; //this is the shadow around the cell itself (cannot have round corners with borders and shadow, need to use two views cell.layer.shadowRadius = 2; cell.layer.cornerRadius = 12; cell.layer.masksToBounds = NO; [[cell layer] setShadowColor:[[UIColor darkGrayColor] CGColor]]; [[cell layer] setShadowOffset:CGSizeMake(0.0,0.0)]; [[cell layer] setShadowOpacity:1.0]; UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:cell.bounds cornerRadius:12]; [[cell layer] setShadowPath:[path CGPath]]; 

Si estás luchando debido a las corners redondeadas vs. las subviews vs. las masksToBounds , intenta usar mi función:

 - (UIView*)putView:(UIView*)view insideShadowWithColor:(UIColor*)color andRadius:(CGFloat)shadowRadius andOffset:(CGSize)shadowOffset andOpacity:(CGFloat)shadowOpacity { CGRect shadowFrame; // Modify this if needed shadowFrame.size.width = 0.f; shadowFrame.size.height = 0.f; shadowFrame.origin.x = 0.f; shadowFrame.origin.y = 0.f; UIView * shadow = [[UIView alloc] initWithFrame:shadowFrame]; shadow.userInteractionEnabled = NO; // Modify this if needed shadow.layer.shadowColor = color.CGColor; shadow.layer.shadowOffset = shadowOffset; shadow.layer.shadowRadius = shadowRadius; shadow.layer.masksToBounds = NO; shadow.clipsToBounds = NO; shadow.layer.shadowOpacity = shadowOpacity; [view.superview insertSubview:shadow belowSubview:view]; [shadow addSubview:view]; return shadow; } 

llámalo en tu vista. si su vista tiene esquinas redondeadas, no importa su tamaño, su forma, se dibujará una sombra agradable.

Simplemente mantenga el valor de retorno de la función para que pueda consultarla cuando desee eliminar la tabla (o, por ejemplo, use insertSubview:aboveView:

Creé un ayudante en UIView

 @interface UIView (Helper) - (void)roundCornerswithRadius:(float)cornerRadius andShadowOffset:(float)shadowOffset; @end 

puedes llamarlo así

 [self.view roundCornerswithRadius:5 andShadowOffset:5]; 

Aquí está la implementación

 - (void)roundCornerswithRadius:(float)cornerRadius andShadowOffset:(float)shadowOffset { const float CORNER_RADIUS = cornerRadius; const float SHADOW_OFFSET = shadowOffset; const float SHADOW_OPACITY = 0.5; const float SHADOW_RADIUS = 3.0; UIView *superView = self.superview; CGRect oldBackgroundFrame = self.frame; [self removeFromSuperview]; CGRect frameForShadowView = CGRectMake(0, 0, oldBackgroundFrame.size.width, oldBackgroundFrame.size.height); UIView *shadowView = [[UIView alloc] initWithFrame:frameForShadowView]; [shadowView.layer setShadowOpacity:SHADOW_OPACITY]; [shadowView.layer setShadowRadius:SHADOW_RADIUS]; [shadowView.layer setShadowOffset:CGSizeMake(SHADOW_OFFSET, SHADOW_OFFSET)]; [self.layer setCornerRadius:CORNER_RADIUS]; [self.layer setMasksToBounds:YES]; [shadowView addSubview:self]; [superView addSubview:shadowView]; } 

Con Swift 4 y Xcode 9 , este es un ejemplo práctico de redondear un ImageView con una sombra paralela y un borde.

  //set dimensions and position of image (in this case, centered) let imageHeight: CGFloat = 150, imageWidth: CGFloat = 150 let xPosition = (self.view.frame.width / 2) - (imageWidth / 2) let yPosition = (self.view.frame.height / 2) - (imageHeight / 2) //set desired corner radius let cornerRadius: CGFloat = 20 //create container for the image let imageContainer = UIView(frame: CGRect(x: xPosition, y: yPosition, width: imageWidth, height: imageHeight)) //configure the container imageContainer.clipsToBounds = false imageContainer.layer.shadowColor = UIColor.black.cgColor imageContainer.layer.shadowOpacity = 1 imageContainer.layer.shadowOffset = CGSize(width: 3.0, height: 3.0) imageContainer.layer.shadowRadius = 5 imageContainer.layer.shadowPath = UIBezierPath(roundedRect: imageContainer.bounds, cornerRadius: cornerRadius).cgPath //create imageView let imageView = UIImageView(frame: imageContainer.bounds) //configure the imageView imageView.clipsToBounds = true imageView.layer.cornerRadius = cornerRadius //add a border (if required) imageView.layer.borderColor = UIColor.black.cgColor imageView.layer.borderWidth = 1.0 //set the image imageView.image = UIImage(named: "bird") //add the views to the superview view.addSubview(imageContainer) imageContainer.addSubview(imageView) 

enter image description here

Si desea que la imagen sea circular: (y se muestra sin borde)

 let cornerRadius = imageWidth / 2 

enter image description here

Después de un día completo de investigación de la vista de esquina redonda con sombra, me complace publicar aquí mi clase de uiview personalizada, espero terminar esta pregunta:

RoundCornerShadowView.h

 #import  @interface RoundCornerShadowView : UIView @end 

RoundCornerShadowView.m

 #import "RoundCornerShadowView.h" @implementation RoundCornerShadowView // *** must override this method, not the other method *** // otherwise, the background corner doesn't disappear.... // @2015/05/29 -(void) layoutSubviews { [super layoutSubviews];//is must to ensure rightly layout children view //1. first, create Inner layer with content CALayer *innerView = [CALayer layer]; innerView.frame = CGRectMake(0,0,self.bounds.size.width,self.bounds.size.height); //instead of: innerView.frame = self.frame; innerView.borderWidth = 1.0f; innerView.cornerRadius = 6.0f; innerView.masksToBounds = YES; innerView.borderColor = [[UIColor lightGrayColor] CGColor]; innerView.backgroundColor = [[UIColor whiteColor] CGColor]; //put the layer to the BOTTOM of layers is also a MUST step... //otherwise this layer will overlay the sub uiviews in current uiview... [self.layer insertSublayer:innerView atIndex:0]; //2. then, create shadow with self layer self.layer.masksToBounds = NO; self.layer.shadowColor = [[UIColor darkGrayColor] CGColor]; self.layer.shadowOpacity = 0.4f; //shadow length self.layer.shadowRadius = 2.0f; //no offset self.layer.shadowOffset = CGSizeMake(0, 0); //right down shadow //[self.layer setShadowOffset: CGSizeMake(1.0f, 1.0f)]; //3. last but important, MUST clear current view background color, or the color will show in the corner! self.backgroundColor = [UIColor clearColor]; } @end 

por lo tanto, NO es necesario agregar una subvista a la vista o debajo de la vista de destino, solo agregue una capa en la vista actual y realice 3 pasos para completarla.

Eche un vistazo de cerca a los comentarios en el código, ¡es útil entender el componente!

Solución Swift 3 & IBInspectable:
Inspirado por la solución de Ade

Primero, crea una extensión UIView:

 // // UIView-Extension.swift // import Foundation import UIKit @IBDesignable extension UIView { // Shadow @IBInspectable var shadow: Bool { get { return layer.shadowOpacity > 0.0 } set { if newValue == true { self.addShadow() } } } fileprivate func addShadow(shadowColor: CGColor = UIColor.black.cgColor, shadowOffset: CGSize = CGSize(width: 3.0, height: 3.0), shadowOpacity: Float = 0.35, shadowRadius: CGFloat = 5.0) { let layer = self.layer layer.masksToBounds = false layer.shadowColor = shadowColor layer.shadowOffset = shadowOffset layer.shadowRadius = shadowRadius layer.shadowOpacity = shadowOpacity layer.shadowPath = UIBezierPath(roundedRect: layer.bounds, cornerRadius: layer.cornerRadius).cgPath let backgroundColor = self.backgroundColor?.cgColor self.backgroundColor = nil layer.backgroundColor = backgroundColor } // Corner radius @IBInspectable var circle: Bool { get { return layer.cornerRadius == self.bounds.width*0.5 } set { if newValue == true { self.cornerRadius = self.bounds.width*0.5 } } } @IBInspectable var cornerRadius: CGFloat { get { return self.layer.cornerRadius } set { self.layer.cornerRadius = newValue } } // Borders // Border width @IBInspectable public var borderWidth: CGFloat { set { layer.borderWidth = newValue } get { return layer.borderWidth } } // Border color @IBInspectable public var borderColor: UIColor? { set { layer.borderColor = newValue?.cgColor } get { if let borderColor = layer.borderColor { return UIColor(cgColor: borderColor) } return nil } } } 

Luego, simplemente seleccione su UIView en la sombra de configuración del generador de interfaz ENCENDIDO y el radio de la esquina , como a continuación:

Seleccionando su UIView

Configuración de sombra ON y radio de esquina

¡El resultado!

Resultado

Algo rápido probado en swift 4

 import UIKit extension UIView { @IBInspectable var dropShadow: Bool { set{ if newValue { layer.shadowColor = UIColor.black.cgColor layer.shadowOpacity = 0.4 layer.shadowRadius = 1 layer.shadowOffset = CGSize.zero } else { layer.shadowColor = UIColor.clear.cgColor layer.shadowOpacity = 0 layer.shadowRadius = 0 layer.shadowOffset = CGSize.zero } } get { return layer.shadowOpacity > 0 } } } 

Produce

enter image description here

Si lo habilita en el Inspector de esta manera:

enter image description here

Se agregará el atributo de tiempo de ejecución definido por el usuario, lo que resulta en:

enter image description here

( cornerRadius = 8 anteriormente la cornerRadius = 8 )

🙂

Shadow + Border + Radio de la esquina enter image description here

  scrollview.backgroundColor = [UIColor whiteColor]; CALayer *ScrlViewLayer = [scrollview layer]; [ScrlViewLayer setMasksToBounds:NO ]; [ScrlViewLayer setShadowColor:[[UIColor lightGrayColor] CGColor]]; [ScrlViewLayer setShadowOpacity:1.0 ]; [ScrlViewLayer setShadowRadius:6.0 ]; [ScrlViewLayer setShadowOffset:CGSizeMake( 0 , 0 )]; [ScrlViewLayer setShouldRasterize:YES]; [ScrlViewLayer setCornerRadius:5.0]; [ScrlViewLayer setBorderColor:[UIColor lightGrayColor].CGColor]; [ScrlViewLayer setBorderWidth:1.0]; [ScrlViewLayer setShadowPath:[UIBezierPath bezierPathWithRect:scrollview.bounds].CGPath]; 

Aquí está mi versión en Swift 3 para una UIView

 let corners:UIRectCorner = [.bottomLeft, .topRight] let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius)) let mask = CAShapeLayer() mask.path = path.cgPath mask.fillColor = UIColor.white.cgColor let shadowLayer = CAShapeLayer() shadowLayer.shadowColor = UIColor.black.cgColor shadowLayer.shadowOffset = CGSize(width: 0.0, height: 4.0) shadowLayer.shadowRadius = 6.0 shadowLayer.shadowOpacity = 0.25 shadowLayer.shadowPath = mask.path self.layer.insertSublayer(shadowLayer, at: 0) self.layer.insertSublayer(mask, at: 1) 

Bueno, si no quieres cambiar tus plumillas y ver la jerarquía como sugirió David C., este método lo hará por ti. Para agregar esquinas redondeadas y sombras a su UIImageView solo use este método, por ejemplo:

 [Utils roundCornersForImageView:myImageView withCornerRadius:6.0 andShadowOffset:2.0]; 

(!) Por motivos de rendimiento, no creo que sea una buena idea usar este código en algo como UITableView, ya que este código cambia la jerarquía de vistas. Por lo tanto, le sugiero que cambie su plumilla y agregue una vista de contenedor para el efecto sombra y use el código Davic C.

 + (void)roundCornersForImageView:(UIImageView *)imageView withCornerRadius:(float)cornerRadius andShadowOffset:(float)shadowOffset { const float CORNER_RADIUS = cornerRadius; const float BORDER_WIDTH = 1.0; const float SHADOW_OFFSET = shadowOffset; const float SHADOW_OPACITY = 0.8; const float SHADOW_RADIUS = 3.0; //Our old image now is just background image view with shadow UIImageView *backgroundImageView = imageView; UIView *superView = backgroundImageView.superview; //Make wider actual visible rect taking into account shadow //offset CGRect oldBackgroundFrame = backgroundImageView.frame; CGRect newBackgroundFrame = CGRectMake(oldBackgroundFrame.origin.x, oldBackgroundFrame.origin.y, oldBackgroundFrame.size.width + SHADOW_OFFSET, oldBackgroundFrame.size.height + SHADOW_OFFSET); [backgroundImageView removeFromSuperview]; backgroundImageView.frame = newBackgroundFrame; //Make new UIImageView with rounded corners and put our old image CGRect frameForRoundedImageView = CGRectMake(0, 0, oldBackgroundFrame.size.width, oldBackgroundFrame.size.height); UIImageView *roundedImageView = [[UIImageView alloc]initWithFrame:frameForRoundedImageView]; roundedImageView.image = imageView.image; [roundedImageView.layer setCornerRadius:CORNER_RADIUS]; [roundedImageView.layer setBorderColor:[UIColor lightGrayColor].CGColor]; [roundedImageView.layer setBorderWidth:BORDER_WIDTH]; [roundedImageView.layer setMasksToBounds:YES]; //Set shadow preferences [backgroundImageView setImage:nil]; [backgroundImageView.layer setShadowColor:[UIColor blackColor].CGColor]; [backgroundImageView.layer setShadowOpacity:SHADOW_OPACITY]; [backgroundImageView.layer setShadowRadius:SHADOW_RADIUS]; [backgroundImageView.layer setShadowOffset:CGSizeMake(SHADOW_OFFSET, SHADOW_OFFSET)]; //Add out two image views back to the view hierarchy. [backgroundImageView addSubview:roundedImageView]; [superView addSubview:backgroundImageView]; } 

Hilo viejo todavía actual …

He editado el método de Daniel Gindi para que sea posible usarlo con botones, etc. Si alguien necesita esquinas redondeadas o desea combinar esquinas redondeadas y un borde, tiene que establecerse en la capa de la vista que se pasa a este método. También he configurado la rasterización para acelerarlo un poco.

 + (UIView*)putView:(UIView*)view insideShadowWithColor:(CGColorRef)color andRadius:(CGFloat)shadowRadius andOffset:(CGSize)shadowOffset andOpacity:(CGFloat)shadowOpacity { // Must have same position like "view" UIView *shadow = [[UIView alloc] initWithFrame:view.frame]; shadow.layer.contentsScale = [UIScreen mainScreen].scale; shadow.userInteractionEnabled = YES; // Modify this if needed shadow.layer.shadowColor = color; shadow.layer.shadowOffset = shadowOffset; shadow.layer.shadowRadius = shadowRadius; shadow.layer.masksToBounds = NO; shadow.clipsToBounds = NO; shadow.layer.shadowOpacity = shadowOpacity; shadow.layer.rasterizationScale = [UIScreen mainScreen].scale; shadow.layer.shouldRasterize = YES; [view.superview insertSubview:shadow belowSubview:view]; [shadow addSubview:view]; // Move view to the top left corner inside the shadowview // ---> Buttons etc are working again :) view.frame = CGRectMake(0, 0, view.frame.size.width, view.frame.size.height); return shadow; } 

Aquí está la solución para el problema de conflicto masksToBounds, funciona para mí.

Después de configurar el corderRadius / borderColor / shadow y así sucesivamente, establezca masksToBounds como NO:

 v.layer.masksToBounds = NO; 

Lo siguiente funcionó mejor para mí (este código se encuentra en la extensión UIView, por lo que self denota algunos UIView a los que debemos agregar una sombra y una esquina)

 - (void)addShadowViewWithCornerRadius:(CGFloat)radius { UIView *container = self.superview; if (!container) { return; } UIView *shadowView = [[UIView alloc] init]; shadowView.translatesAutoresizingMaskIntoConstraints = NO; shadowView.backgroundColor = [UIColor lightGrayColor]; shadowView.layer.cornerRadius = radius; shadowView.layer.masksToBounds = YES; [container addSubview:shadowView]; [container bringSubviewToFront:shadowView]; [container addConstraint:[NSLayoutConstraint constraintWithItem:shadowView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]]; [container addConstraint:[NSLayoutConstraint constraintWithItem:shadowView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:2.0]]; [container addConstraint:[NSLayoutConstraint constraintWithItem:shadowView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]]; [container addConstraint:[NSLayoutConstraint constraintWithItem:shadowView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:2.0]]; [container sendSubviewToBack:shadowView]; } 

La principal diferencia entre este y otros ejemplos de código es que esto agrega la vista sombreada como una vista hermana (en lugar de agregar la vista actual como subvista de la vista sombreada), eliminando así la necesidad de modificar la jerarquía de vista existente de cualquier manera.

Swift 4: crear la subclase de UIView

 class ShadowView: UIView { required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) // corner radius self.layer.cornerRadius = 10 // border self.layer.borderWidth = 1.0 self.layer.borderColor = UIColor.black.cgColor // shadow self.layer.shadowColor = UIColor.black.cgColor self.layer.shadowOffset = CGSize(width: 3, height: 3) self.layer.shadowOpacity = 0.7 self.layer.shadowRadius = 4.0 } } 

Utilizando..

Utilice la vista de sombras de clase

Debes usar el uso de shadowView y roundView

enter image description here

shadowView

  • Debe tener color de fondo
  • Debería estar detrás de roundView
  • El truco es diseñar shadowView un poco dentro, y su sombra necesita brillar. Ajuste las insets para que shadowView sea ​​completamente invisible detrás de roundView

roundView

  • Debe subir las subvistas de los clips

El código

 addSubviews(shadowView, roundView) roundView.addSubviews(titleLabel, subtitleLabel, imageView) // need inset shadowView.pinEdges(view: self, inset: UIEdgeInsets(constraintInsets: 2)) roundView.pinEdges(view: self) do { shadowView.backgroundColor = .white // need background let layer = shadowView.layer layer.shadowColor = UIColor.black.cgColor layer.shadowRadius = 3 layer.shadowOffset = CGSize(width: 3, height: 3) layer.shadowOpacity = 0.7 layer.shouldRasterize = true } do { roundView.backgroundColor = .white let layer = roundView.layer layer.masksToBounds = true layer.cornerRadius = 5 } 

O puede hacer lo siguiente sin especificar clipToBounds/maskToBounds

 layer.shadowColor = UIColor.gray.cgColor layer.shadowOffset = CGSize(width: 3, height: 3) layer.shadowOpacity = 0.8 

¡La respuesta de daniel.gindi arriba hizo el truco para mí! (+1 daniel) Sin embargo, tuve que hacer ajustes menores: cambiar el tamaño de shadowFrame para que sea igual al tamaño de fotogtwig de view y permitir la interacción del usuario. Aquí está el código actualizado:

 + (UIView*)putView:(UIView*)view insideShadowWithColor:(UIColor*)color andRadius:(CGFloat)shadowRadius andOffset:(CGSize)shadowOffset andOpacity:(CGFloat)shadowOpacity { CGRect shadowFrame; // Modify this if needed // Modified this line shadowFrame.size = CGSizeMake(view.frame.size.width, view.frame.size.height); shadowFrame.origin.x = 0.f; shadowFrame.origin.y = 0.f; UIView * shadow = [[UIView alloc] initWithFrame:shadowFrame]; // Modified this line shadow.userInteractionEnabled = YES; shadow.layer.shadowColor = color.CGColor; shadow.layer.shadowOffset = shadowOffset; shadow.layer.shadowRadius = shadowRadius; shadow.layer.masksToBounds = NO; shadow.clipsToBounds = NO; shadow.layer.shadowOpacity = shadowOpacity; [shadow addSubview:view]; return shadow; } 

Me gustaría agregar que en mi caso, estaba tratando de agregar esto a un controlador de vista de terceros, es decir, no tenía control directo sobre el código. Entonces, así es como utilicé la función anterior:

 UIView *shadow = [self putView:vc.view insideShadowWithColor:[UIColor blackColor] andRadius:5.0 andOffset:CGSizeMake(0.0, 0.0) andOpacity:1.0]; vc.view = shadow; vc.view.layer.cornerRadius = 5.0; vc.view.layer.masksToBounds = YES; 

I make some changes to the code of daniel.gindi

This is all you need to make it work.

 + (void)putView:(UIView*)view insideShadowWithColor:(UIColor*)color andBlur: (CGFloat)blur andOffset:(CGSize)shadowOffset andOpacity:(CGFloat)shadowOpacity { CGRect shadowFrame = view.frame; UIView * shadow = [[UIView alloc] initWithFrame:shadowFrame]; shadow.backgroundColor = [UIColor redColor]; shadow.userInteractionEnabled = YES; // Modify this if needed shadow.layer.shadowColor = color.CGColor; shadow.layer.shadowOffset = shadowOffset; shadow.layer.shadowRadius = blur; shadow.layer.cornerRadius = view.layer.cornerRadius; shadow.layer.masksToBounds = NO; shadow.clipsToBounds = NO; shadow.layer.shadowOpacity = shadowOpacity; [view.superview insertSubview:shadow belowSubview:view]; } 

You need to use two UIViews to achieve this. One UIView will work like shadow and other one will work for rounded border.

Here is a code snippet a Class Method with a help of a protocol :

 @implementation UIMethods + (UIView *)genComposeButton:(UIViewController  *)observer; { UIView *shadow = [[UIView alloc]init]; shadow.layer.cornerRadius = 5.0; shadow.layer.shadowColor = [[UIColor blackColor] CGColor]; shadow.layer.shadowOpacity = 1.0; shadow.layer.shadowRadius = 10.0; shadow.layer.shadowOffset = CGSizeMake(0.0f, -0.5f); UIButton *btnCompose = [[UIButton alloc]initWithFrame:CGRectMake(0, 0,60, 60)]; [btnCompose setUserInteractionEnabled:YES]; btnCompose.layer.cornerRadius = 30; btnCompose.layer.masksToBounds = YES; [btnCompose setImage:[UIImage imageNamed:@"60x60"] forState:UIControlStateNormal]; [btnCompose addTarget:observer action:@selector(btnCompose_click:) forControlEvents:UIControlEventTouchUpInside]; [shadow addSubview:btnCompose]; return shadow; } 

In the code above btnCompose_click: will become a @required delegate method which will fire on the button click.

And here I added a button to my UIViewController like this:

 UIView *btnCompose = [UIMethods genComposeButton:self]; btnCompose.frame = CGRectMake(self.view.frame.size.width - 75, self.view.frame.size.height - 75, 60, 60); [self.view addSubview:btnCompose]; 

El resultado se verá así:

enter image description here

I have tried so many solutions from this post and ended up with the below solution. This is full proof solution unless you need to drop shadow on a clear color view .

 - (void)addShadowWithRadius:(CGFloat)shadowRadius withOpacity:(CGFloat)shadowOpacity withOffset:(CGSize)shadowOffset withColor:(UIColor *)shadowColor withCornerradius:(CGFloat)cornerRadius { UIView *viewShadow = [[UIView alloc]initWithFrame:self.frame]; viewShadow.backgroundColor = [UIColor whiteColor]; viewShadow.layer.shadowColor = shadowColor.CGColor; viewShadow.layer.shadowOffset = shadowOffset; viewShadow.layer.shadowRadius = shadowRadius; viewShadow.layer.shadowOpacity = shadowOpacity; viewShadow.layer.cornerRadius = cornerRadius; viewShadow.layer.masksToBounds = NO; [self.superview insertSubview:viewShadow belowSubview:self]; [viewShadow setTranslatesAutoresizingMaskIntoConstraints:NO]; [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:viewShadow attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]]; [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:viewShadow attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0]]; [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:viewShadow attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:viewShadow attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]]; [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:viewShadow attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:viewShadow attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]]; [self layoutIfNeeded]; self.layer.cornerRadius = cornerRadius; self.layer.masksToBounds = YES; } 

The answer provided by Evan Mulawski will work perfectly. The catch is that you have to set the background color for the view to clearColor and the masksToBounds property to NO.

You can set whatever color you want for the view, set it like

 v.layer.backgroundColor = your color; 

Espero que esto ayude..

This is how you do it, with rounded corners and rounded shadows without bothering with paths.

 //Inner view with content [imageView.layer setBorderColor:[[UIColor lightGrayColor] CGColor]]; [imageView.layer setBorderWidth:1.0f]; [imageView.layer setCornerRadius:8.0f]; [imageView.layer setMasksToBounds:YES]; //Outer view with shadow UIView* shadowContainer = [[UIView alloc] initWithFrame:imageView.frame]; [shadowContainer.layer setMasksToBounds:NO]; [shadowContainer.layer setShadowColor:[[UIColor blackColor] CGColor]]; [shadowContainer.layer setShadowOpacity:0.6f]; [shadowContainer.layer setShadowRadius:2.0f]; [shadowContainer.layer setShadowOffset: CGSizeMake(0.0f, 2.0f)]; [shadowContainer addSubview:imageView]; 

The view with content, in my case a UIImageView, has a corner radius and therefore has to mask to bounds.

We create another equally sized view for the shadows, set it’s maskToBounds to NO and then add the content view to the container view (eg shadowContainer).

I write this UIView category method to solve this problem, uses separate views for the shadow and the corner radius.

 -(UIView *)shadowedWrapViewWithBounds:(CGRect)bounds { UIView *baseView = [[UIView alloc] init]; baseView.bounds = bounds; baseView.backgroundColor = [UIColor clearColor]; baseView.layer.shadowColor = [UIColor blackColor].CGColor; baseView.layer.shadowOffset = CGSizeMake(0, 0); baseView.layer.shadowOpacity = 0.7; baseView.layer.shadowRadius = 4.0; // improve performance baseView.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:baseView.bounds cornerRadius:4].CGPath; baseView.layer.shouldRasterize = YES; baseView.layer.rasterizationScale = [UIScreen mainScreen].scale; [baseView addSubview:self]; //use Masonry autolayout, self can set corner radius [self makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(baseView); }]; return baseView; } 

You need add masksToBounds = true for combined between corderRadius shadowRadius .

 button.layer.masksToBounds = false; 

Swift 4 Solution for making UICollectionViewCell round and adding Shadows , without any extensions and complications 🙂

Note: For simple views eg Buttons. See the @suragch’s Answer in this post. https://stackoverflow.com/a/34984063/7698092 . Tested successfully for buttons

In case if any one still struggling to round the corners and add shadows at the same time. Although this solution works with UICollectionViewCell, it can be generalized to any view.

This technique worked for me without making any extensions and all the complicated stuff. I am working with storyBoard.

Técnica

You must add a UIView (lets say it “containerView”) inside your UICollectionViewCell in storyBoard and add all the required views (buttons, images etc) inside this containerView. See the Screenshot. Structure of Cell

Connect the outlet for containerView. Add following lines of code in CellforItemAtIndexPath delegate function.

 //adds shadow to the layer of cell cell.layer.cornerRadius = 3.0 cell.layer.masksToBounds = false cell.layer.shadowColor = UIColor.black.cgColor cell.layer.shadowOffset = CGSize(width: 0, height: 0) cell.layer.shadowOpacity = 0.6 //makes the cell round let containerView = cell.containerView! containerView.layer.cornerRadius = 8 containerView.clipsToBounds = true 

Salida

See the simulator Screenshot Rounded corners with Shadows (UICollectionViewCell)

 extension UIView { func dropRoundedShadowForAllSides() { let backgroundView = UIView(frame:self.frame) let radius = frame.height/2 backgroundView.layer.masksToBounds = false self.layer.masksToBounds = true backgroundView.layer.shadowOffset = CGSize(width: 0.0, height: 0.0) backgroundView.layer.shadowRadius = 4 backgroundView.layer.shadowOpacity = 0.4 let path = UIBezierPath() // Start at the Top Left Corner + radius distance path.move(to: CGPoint(x: 2*radius, y: 0.0)) // Move to the Top Right Corner - radius distance path.addLine(to: CGPoint(x: backgroundView.frame.size.width - radius, y: 0.0)) // Move to top right corner + radius down as curve let centerPoint1 = CGPoint(x:backgroundView.frame.size.width - radius,y:radius) path.addArc(withCenter: centerPoint1, radius: radius, startAngle: 3*(.pi/2), endAngle: 0, clockwise: true) // Move to the Bottom Right Corner - radius path.addLine(to: CGPoint(x: backgroundView.frame.size.width, y: backgroundView.frame.size.height - radius)) // Move to top right corner + radius left as curve let centerPoint2 = CGPoint(x:backgroundView.frame.size.width - radius,y:backgroundView.frame.size.height - radius) path.addArc(withCenter: centerPoint2, radius: radius, startAngle: 0, endAngle: .pi/2, clockwise: true) // Move to the Bottom Left Corner - radius path.addLine(to: CGPoint(x: radius, y: backgroundView.frame.size.height)) // Move to left right corner - radius up as curve let centerPoint3 = CGPoint(x:radius,y:backgroundView.frame.size.height - radius) path.addArc(withCenter: centerPoint3, radius: radius, startAngle: .pi/2, endAngle: .pi, clockwise: true) // Move to the top Left Corner - radius path.addLine(to: CGPoint(x: 0, y: radius)) // Move to top right corner + radius down as curve let centerPoint4 = CGPoint(x:radius,y:radius) path.addArc(withCenter: centerPoint4, radius: radius, startAngle: .pi, endAngle: 3 * (.pi/2), clockwise: true) path.close() backgroundView.layer.shadowPath = path.cgPath if let superView = self.superview { superView.addSubview(backgroundView) superView.sendSubview(toBack: backgroundView) superView.bringSubview(toFront: self) } } }