iphone - puedo - UIView redondeado usando CALayers-solo algunas esquinas-¿Cómo?
no me aparece el centro de control iphone (13)
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?
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];
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];
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;
En iOS 11, ahora solo podemos redondear algunas esquinas
let view = UIView()
view.clipsToBounds = true
view.layer.cornerRadius = 8
view.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]
Extensión CALayer con sintaxis 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))
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
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];
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.
Para agregar a la answer y la addition , 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)
}
}
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.
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 rectángulos 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.
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 ).
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.