UIView redondeado usando CALayers – solo algunas esquinas – ¿Cómo?

En mi aplicación, hay cuatro botones nombrados de la siguiente manera:

  • Arriba a la izquierda
  • Abajo – izquierda
  • Parte superior derecha
  • Abajo a la derecha

Sobre los botones hay una vista de imagen (o una UIView).

Ahora, supongamos que un usuario toca el botón arriba – izquierda. La imagen / vista arriba debe redondearse en esa esquina particular.

Tengo dificultades para aplicar esquinas redondeadas a UIView.

En este momento estoy usando el siguiente código para aplicar las esquinas redondeadas a cada vista:

// imgVUserImg is a image view on IB. imgVUserImg.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"any Url Here"]; CALayer *l = [imgVUserImg layer]; [l setMasksToBounds:YES]; [l setCornerRadius:5.0]; [l setBorderWidth:2.0]; [l setBorderColor:[[UIColor darkGrayColor] CGColor]]; 

El código anterior aplica la redondez a cada una de las esquinas de la Vista suministrada. En cambio, solo quería aplicar redondez a las esquinas seleccionadas, como – arriba / arriba + izquierda / abajo + derecha, etc.

¿Es posible? ¿Cómo?

Utilicé la respuesta en ¿Cómo creo una UILabel acorralada en el iPhone? y el código de ¿Cómo se hace una vista recta redondeada con transparencia en el iPhone? para hacer este código

Luego me di cuenta de que había respondido la pregunta incorrecta (dio una UILabel redondeada en lugar de UIImage), así que usé este código para cambiarla:

http://discussions.apple.com/thread.jspa?threadID=1683876

Haz un proyecto de iPhone con la plantilla Ver. En el controlador de vista, agregue esto:

 - (void)viewDidLoad { CGRect rect = CGRectMake(10, 10, 200, 100); MyView *myView = [[MyView alloc] initWithFrame:rect]; [self.view addSubview:myView]; [super viewDidLoad]; } 

MyView es solo una subclase de UIImageView :

 @interface MyView : UIImageView { } 

Nunca antes había usado contextos gráficos, pero logré coartar este código. Falta el código de dos de las esquinas. Si lees el código, puedes ver cómo lo implementé (eliminando algunas de las llamadas CGContextAddArc y borrando algunos de los valores de radio en el código. El código para todas las esquinas está ahí, así que CGContextAddArc como punto de partida y elimina las partes que crean esquinas que no necesita. Tenga en cuenta que también puede hacer rectangularjs con 2 o 3 esquinas redondeadas si lo desea.

El código no es perfecto, pero estoy seguro de que puedes arreglarlo un poco.

 static void addRoundedRectToPath(CGContextRef context, CGRect rect, float radius, int roundedCornerPosition) { // all corners rounded // CGContextMoveToPoint(context, rect.origin.x, rect.origin.y + radius); // CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height - radius); // CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, // radius, M_PI / 4, M_PI / 2, 1); // CGContextAddLineToPoint(context, rect.origin.x + rect.size.width - radius, // rect.origin.y + rect.size.height); // CGContextAddArc(context, rect.origin.x + rect.size.width - radius, // rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1); // CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + radius); // CGContextAddArc(context, rect.origin.x + rect.size.width - radius, rect.origin.y + radius, // radius, 0.0f, -M_PI / 2, 1); // CGContextAddLineToPoint(context, rect.origin.x + radius, rect.origin.y); // CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + radius, radius, // -M_PI / 2, M_PI, 1); // top left if (roundedCornerPosition == 1) { CGContextMoveToPoint(context, rect.origin.x, rect.origin.y + radius); CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height - radius); CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, radius, M_PI / 4, M_PI / 2, 1); CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height); CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y); CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y); } // bottom left if (roundedCornerPosition == 2) { CGContextMoveToPoint(context, rect.origin.x, rect.origin.y); CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height); CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height); CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y); CGContextAddLineToPoint(context, rect.origin.x + radius, rect.origin.y); CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + radius, radius, -M_PI / 2, M_PI, 1); } // add the other corners here CGContextClosePath(context); CGContextRestoreGState(context); } -(UIImage *)setImage { UIImage *img = [UIImage imageNamed:@"my_image.png"]; int w = img.size.width; int h = img.size.height; CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst); CGContextBeginPath(context); CGRect rect = CGRectMake(0, 0, w, h); addRoundedRectToPath(context, rect, 50, 1); CGContextClosePath(context); CGContextClip(context); CGContextDrawImage(context, rect, img.CGImage); CGImageRef imageMasked = CGBitmapContextCreateImage(context); CGContextRelease(context); CGColorSpaceRelease(colorSpace); [img release]; return [UIImage imageWithCGImage:imageMasked]; } 

texto alternativo http://nevan.net/skitch/skitched-20100224-092237.png

No olvide que necesitará obtener el marco QuartzCore para que esto funcione.

A partir de iOS 3.2, puede usar la funcionalidad de UIBezierPath s para crear un rect redondeado UIBezierPath para usar (donde solo se redondean las esquinas que especifique). A continuación, puede usar esto como la ruta de un CAShapeLayer , y usar esto como una máscara para la capa de su vista:

 // Create the path (with only the top-left corner rounded) UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds byRoundingCorners:UIRectCornerTopLeft cornerRadii:CGSizeMake(10.0, 10.0)]; // Create the shape layer and set its path CAShapeLayer *maskLayer = [CAShapeLayer layer]; maskLayer.frame = imageView.bounds; maskLayer.path = maskPath.CGPath; // Set the newly created shape layer as the mask for the image view's layer imageView.layer.mask = maskLayer; 

Y eso es todo, sin perder el tiempo definiendo formas manualmente en Core Graphics, sin crear imágenes de enmascaramiento en Photoshop. La capa ni siquiera necesita invalidación. Aplicar la esquina redondeada o cambiar a una nueva esquina es tan simple como definir una nueva UIBezierPath y usar su CGPath como la ruta de la capa de la máscara. El parámetro de corners del bezierPathWithRoundedRect:byRoundingCorners:cornerRadii: es una máscara de bits, por lo que las esquinas múltiples se pueden redondear mediante ORing juntos.


EDITAR: Agregar una sombra

Si está buscando agregar una sombra a esto, se requiere un poco más de trabajo.

Como ” imageView.layer.mask = maskLayer ” aplica una máscara, normalmente no se mostrará una sombra fuera de ella. El truco es usar una vista transparente y luego agregar dos subcapas ( CALayer ) a la capa de la vista: shadowLayer y roundedLayer . Ambos necesitan hacer uso de UIBezierPath . La imagen se agrega como el contenido de roundedLayer .

 // Create a transparent view UIView *theView = [[UIView alloc] initWithFrame:theFrame]; [theView setBackgroundColor:[UIColor clearColor]]; // Create the path (with only the top-left corner rounded) UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:theView.bounds byRoundingCorners:UIRectCornerTopLeft cornerRadii:CGSizeMake(10.0f, 10.0f)]; // Create the shadow layer CAShapeLayer *shadowLayer = [CAShapeLayer layer]; [shadowLayer setFrame:theView.bounds]; [shadowLayer setMasksToBounds:NO]; [shadowLayer setShadowPath:maskPath.CGPath]; // ... // Set the shadowColor, shadowOffset, shadowOpacity & shadowRadius as required // ... // Create the rounded layer, and mask it using the rounded mask layer CALayer *roundedLayer = [CALayer layer]; [roundedLayer setFrame:theView.bounds]; [roundedLayer setContents:(id)theImage.CGImage]; CAShapeLayer *maskLayer = [CAShapeLayer layer]; [maskLayer setFrame:theView.bounds]; [maskLayer setPath:maskPath.CGPath]; roundedLayer.mask = maskLayer; // Add these two layers as sublayers to the view [theView.layer addSublayer:shadowLayer]; [theView.layer addSublayer:roundedLayer]; 

He usado este código en muchos lugares en mi código y funciona 100% correctamente. Puede cambiar cualquier corder cambiando una propiedad “porRoundingCorners: UIRectCornerBottomLeft”

 UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:UIRectCornerBottomLeft cornerRadii:CGSizeMake(10.0, 10.0)]; CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init]; maskLayer.frame = view.bounds; maskLayer.path = maskPath.CGPath; view.layer.mask = maskLayer; [maskLayer release]; 

El ejemplo de Stuarts para redondear esquinas específicas funciona muy bien. Si desea redondear varias esquinas como arriba a la izquierda y derecha, esta es la forma de hacerlo

 // Create the path (with only the top-left corner rounded) UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageview byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight cornerRadii:CGSizeMake(10.0, 10.0)]; // Create the shape layer and set its path CAShapeLayer *maskLayer = [CAShapeLayer layer]; maskLayer.frame = imageview.bounds; maskLayer.path = maskPath.CGPath; // Set the newly created shape layer as the mask for the image view's layer imageview.layer.mask = maskLayer; 

Gracias por compartir. Aquí me gustaría compartir la solución en swift 2.0 para obtener más información sobre este tema. (para conformar el protocolo de UIRectCorner)

 let mp = UIBezierPath(roundedRect: cell.bounds, byRoundingCorners: [.bottomLeft, .TopLeft], cornerRadii: CGSize(width: 10, height: 10)) let ml = CAShapeLayer() ml.frame = self.bounds ml.path = mp.CGPath self.layer.mask = ml 

hay una respuesta más fácil y rápida que puede funcionar dependiendo de sus necesidades y también funciona con sombras. puede establecer maskToBounds en el superlayer en true, y compensar las capas secundarias para que 2 de sus esquinas estén fuera de los límites del superlayer, cortando efectivamente las esquinas redondeadas en 2 lados de distancia.

por supuesto, esto solo funciona cuando desea tener solo 2 esquinas redondeadas en el mismo lado y el contenido de la capa tiene el mismo aspecto cuando corta algunos píxeles de un lado. funciona muy bien para tener gráficos de barras redondeados solo en la parte superior.

Extensión CALayer con syntax Swift 3 y superior

 extension CALayer { func round(roundedRect rect: CGRect, byRoundingCorners corners: UIRectCorner, cornerRadii: CGSize) -> Void { let bp = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: cornerRadii) let sl = CAShapeLayer() sl.frame = self.bounds sl.path = bp.cgPath self.mask = sl } } 

Se puede usar como:

 let layer: CALayer = yourView.layer layer.round(roundedRect: yourView.bounds, byRoundingCorners: [.bottomLeft, .topLeft], cornerRadii: CGSize(width: 5, height: 5)) 

Vea esta pregunta relacionada . Deberá dibujar su propio rectángulo en un CGPath con algunas esquinas redondeadas, agregar el CGPath a su CGContext y luego hacer un clip con CGContextClip .

También puede dibujar el rectángulo redondeado con valores alfa a una imagen y luego usar esa imagen para crear una nueva capa que establezca como propiedad de la mask su capa ( consulte la documentación de Apple ).

En iOS 11, ahora solo podemos redondear algunas esquinas

 let view = UIView() view.clipsToBounds = true view.layer.cornerRadius = 8 view.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner] 

Media década de retraso, pero creo que la forma actual en que la gente hace esto no es 100% correcta. Muchas personas han tenido el problema de que el uso del método UIBezierPath + CAShapeLayer interfiere con el diseño automático, especialmente cuando está configurado en el guión gráfico. No hay respuestas para esto, así que decidí agregar la mía.

Hay una manera muy fácil de eludir eso: Dibuje las esquinas redondeadas en la función drawRect(rect: CGRect) .

Por ejemplo, si quisiera las esquinas redondeadas superiores para una UIView, subclase UIView y luego usaría esa subclase cuando fuera apropiado.

 import UIKit class TopRoundedView: UIView { override func drawRect(rect: CGRect) { super.drawRect(rect) var maskPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: UIRectCorner.TopLeft | UIRectCorner.TopRight, cornerRadii: CGSizeMake(5.0, 5.0)) var maskLayer = CAShapeLayer() maskLayer.frame = self.bounds maskLayer.path = maskPath.CGPath self.layer.mask = maskLayer } } 

Esta es la mejor manera de conquistar el problema y no tarda en adaptarse.

Redondear solo algunas esquinas no funcionará bien con cambio automático de tamaño o diseño automático.

Entonces, otra opción es usar cornerRadius normal y ocultar las esquinas que no desea bajo otra vista o fuera de los límites de su supervista, asegurándose de que esté configurado para recortar sus contenidos.

Para agregar a la respuesta y la adición , creé una UIView simple y reutilizable en Swift. Dependiendo de su caso de uso, es posible que desee realizar modificaciones (evite crear objetos en cada diseño, etc.), pero quería mantenerlo lo más simple posible. La extensión le permite aplicar esto a otras vistas (por ejemplo, UIImageView ) más fácil si no le gusta la UIImageView subclases.

 extension UIView { func roundCorners(_ roundedCorners: UIRectCorner, toRadius radius: CGFloat) { roundCorners(roundedCorners, toRadii: CGSize(width: radius, height: radius)) } func roundCorners(_ roundedCorners: UIRectCorner, toRadii cornerRadii: CGSize) { let maskBezierPath = UIBezierPath( roundedRect: bounds, byRoundingCorners: roundedCorners, cornerRadii: cornerRadii) let maskShapeLayer = CAShapeLayer() maskShapeLayer.frame = bounds maskShapeLayer.path = maskBezierPath.cgPath layer.mask = maskShapeLayer } } class RoundedCornerView: UIView { var roundedCorners: UIRectCorner = UIRectCorner.allCorners var roundedCornerRadii: CGSize = CGSize(width: 10.0, height: 10.0) override func layoutSubviews() { super.layoutSubviews() roundCorners(roundedCorners, toRadii: roundedCornerRadii) } } 

Así es como lo aplicaría a un UIViewController :

 class MyViewController: UIViewController { private var _view: RoundedCornerView { return view as! RoundedCornerView } override func loadView() { view = RoundedCornerView() } override func viewDidLoad() { super.viewDidLoad() _view.roundedCorners = [.topLeft, .topRight] _view.roundedCornerRadii = CGSize(width: 10.0, height: 10.0) } } 

Al completar la respuesta de Stuart, puede tener el método de redondeo de esquina como sigue:

 @implementation UIView (RoundCorners) - (void)applyRoundCorners:(UIRectCorner)corners radius:(CGFloat)radius { UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)]; CAShapeLayer *maskLayer = [CAShapeLayer layer]; maskLayer.frame = self.bounds; maskLayer.path = maskPath.CGPath; self.layer.mask = maskLayer; } @end 

Entonces, para aplicar la esquina de redondeo, simplemente lo hace:

 [self.imageView applyRoundCorners:UIRectCornerTopRight|UIRectCornerTopLeft radius:10];