UIView redondeada usando CALayers - solo algunas esquinas - ¿Cómo?

Resuelto sagarkothari asked hace 14 años • 14 respuestas

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

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

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

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

Tengo algunas dificultades para aplicar esquinas redondeadas a UIView.

Ahora 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 proporcionada. En lugar de eso, solo quería aplicar redondez a las esquinas seleccionadas como: arriba/arriba+izquierda/abajo+derecha, etc.

¿Es posible? ¿Cómo?

sagarkothari avatar Feb 15 '10 11:02 sagarkothari
Aceptado

A partir de iOS 3.2, puede utilizar la funcionalidad de UIBezierPaths para crear un rectángulo redondeado listo para usar (donde solo se redondean las esquinas que especifique). Luego puede usar esto como ruta de a CAShapeLayery usarlo como 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: no hay que perder el tiempo definiendo formas manualmente en Core Graphics, ni crear imágenes enmascaradas en Photoshop. Ni siquiera es necesario invalidar la capa. Aplicar la esquina redondeada o cambiar a una nueva esquina es tan simple como definir una nueva UIBezierPathy usarla CGPathcomo ruta de la capa de máscara. El cornersparámetro del bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:método es una máscara de bits, por lo que se pueden redondear varias esquinas uniéndolas en OR.


EDITAR: Agregar una sombra

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

Debido a que " imageView.layer.mask = maskLayer" aplica una máscara, normalmente no se mostrará una sombra fuera de ella. El truco consiste en utilizar una vista transparente y luego agregar dos subcapas CALayera la capa de la vista: shadowLayery roundedLayer. Ambos necesitan hacer uso de UIBezierPath. La imagen se agrega como 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];
Stuart avatar Apr 29 '2011 00:04 Stuart

Utilicé la respuesta en ¿Cómo creo una UILabel con esquinas redondeadas en el iPhone? y el código de ¿Cómo se realiza 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 (di una UILabel redondeada en lugar de UIImage), así que usé este código para cambiarla:

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

Crea 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];
}

MyViewes solo una UIImageViewsubclase:

@interface MyView : UIImageView
{
}

Nunca antes había usado contextos gráficos, pero logré unir este código. Falta el código de dos de las esquinas. Si lees el código, puedes ver cómo implementé esto (eliminando algunas de las CGContextAddArcllamadas y eliminando algunos de los valores de radio en el código. El código para todas las esquinas está ahí, así que úsalo como punto de partida y elimina el partes que crean esquinas que no necesitas. Ten en cuenta que también puedes hacer rectángulos con 2 o 3 esquinas redondeadas si lo deseas.

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á tener el marco QuartzCore allí para que esto funcione.

nevan king avatar Feb 23 '2010 14:02 nevan king