UIView arredondada usando CALayers – apenas alguns cantos – Como?

Na minha aplicação – existem quatro botões com o seguinte nome:

  • Superior esquerdo
  • Inferior esquerdo
  • Canto superior direito
  • Canto inferior direito

Acima dos botões, há uma visualização de imagem (ou um UIView).

Agora, suponha que um usuário toque no botão esquerdo. A imagem / vista acima deve ser arredondada nesse canto específico.

Estou tendo alguma dificuldade em aplicar cantos arredondados ao UIView.

No momento, estou usando o código a seguir para aplicar os cantos arredondados a cada visualização:

// 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]]; 

O código acima está aplicando a redondeza a cada um dos cantos da visualização fornecida. Em vez disso, eu só queria aplicar a redondeza aos cantos selecionados como – superior / superior + esquerda / inferior + direita etc.

É possível? Como?

Eu usei a resposta no Como faço para criar um UILabel arredondado no iPhone? e o código de Como é uma visão retangular com transparência feita no iphone? para fazer este código.

Então percebi que tinha respondido a pergunta errada (dei uma UILabel arredondada em vez de UIImage) então usei este código para alterá-lo:

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

Faça um projeto do iPhone com o modelo Exibir. No controlador de visualização, adicione isto:

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

MyView é apenas uma subclass de UIImageView :

 @interface MyView : UIImageView { } 

Eu nunca usei contextos charts antes, mas consegui manejar juntos esse código. Está faltando o código para dois dos cantos. Se você ler o código, você pode ver como eu implementei isso (excluindo algumas das chamadas CGContextAddArc e excluindo alguns dos valores do raio no código. O código para todos os cantos está lá, então use isso como ponto de partida e delete). as partes que criam cantos que você não precisa Observe que você pode fazer retângulos com 2 ou 3 cantos arredondados também, se quiser.

O código não é perfeito, mas tenho certeza que você pode arrumar um pouco.

 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

Não se esqueça de que você precisará instalar o framework QuartzCore para que isso funcione.

A partir do iOS 3.2, você pode usar a funcionalidade de UIBezierPath s para criar um UIBezierPath para o uso (onde apenas os cantos especificados são arredondados). Você pode usar isso como o caminho de um CAShapeLayer e usá-lo como uma máscara para a camada de visualização:

 // 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; 

E é isso – sem mexer manualmente definindo formas no Core Graphics, sem criar imagens de mascaramento no Photoshop. A camada nem precisa invalidar. Aplicar o canto arredondado ou mudar para um novo canto é tão simples quanto definir um novo UIBezierPath e usar seu CGPath como o caminho da camada de máscara. O parâmetro corners do bezierPathWithRoundedRect:byRoundingCorners:cornerRadii: é uma máscara de bits e, portanto, vários cantos podem ser arredondados, reunindo-os.


EDIT: Adicionando uma sombra

Se você está procurando adicionar uma sombra a isso, um pouco mais de trabalho é necessário.

Como ” imageView.layer.mask = maskLayer ” aplica uma máscara, uma sombra normalmente não será mostrada fora dela. O truque é usar uma visualização transparente e adicionar duas subcamadas ( CALayer s) à camada da view: shadowLayer e roundedLayer . Ambos precisam fazer uso do UIBezierPath . A imagem é adicionada como o conteúdo 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]; 

Eu usei esse código em muitos lugares no meu código e funciona 100% corretamente. Você pode alterar qualquer corder por uma propriedade alterada “byRoundingCorners: 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]; 

O exemplo Stuarts para arredondar cantos específicos funciona muito bem. Se você quiser arredondar vários cantos como superior esquerdo e direito, é como fazer isso

 // 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; 

Obrigado por compartilhar. Aqui eu gostaria de compartilhar a solução no swift 2.0 para mais referências sobre este assunto. (para conformar o protocolo do 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 

existe uma resposta mais fácil e rápida que pode funcionar dependendo das suas necessidades e também funciona com sombras. você pode definir maskToBounds na superlayer como true e compensar as camadas-filho de modo que dois de seus cantos fiquem fora dos limites da superlayer, cortando efetivamente os cantos arredondados em 2 lados.

é claro que isso só funciona quando você quer ter apenas 2 cantos arredondados no mesmo lado e o conteúdo da camada parece o mesmo quando você corta alguns pixels de um lado. funciona muito bem para ter charts de barras arredondados apenas no lado superior.

Extensão de CALayer com syntax Swift 3 e acima

 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 } } 

Pode ser usado como:

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

Veja esta questão relacionada . Você terá que desenhar seu próprio retângulo em um CGPath com alguns cantos arredondados, adicionar o CGPath ao seu CGContext e então CGContext -lo usando CGContextClip .

Você também pode desenhar o retângulo com valores alfa em uma imagem e, em seguida, usar essa imagem para criar uma nova camada definida como a propriedade de mask da camada ( consulte a documentação da Apple ).

No iOS 11, agora podemos arredondar apenas alguns cantos

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

Meia década atrasada, mas acho que a maneira atual como as pessoas fazem isso não é 100% correta. Muitas pessoas tiveram o problema de que o uso do método UIBezierPath + CAShapeLayer interfere no layout automático, especialmente quando ele é definido no Storyboard. Nenhuma resposta passa por isso, então decidi adicionar a minha.

Existe uma maneira muito fácil de contornar isso: Desenhe os cantos arredondados na function drawRect(rect: CGRect) .

Por exemplo, se eu quisesse cantos arredondados para um UIView, eu subclassificaria o UIView e usaria essa subclass sempre que fosse apropriado.

 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 é a melhor maneira de vencer o problema e não precisa de tempo para se adaptar.

Arredondar apenas alguns cantos não funciona com redimensionamento automático ou layout automático.

Portanto, outra opção é usar o cornerRadius regular e ocultar os cantos que você não deseja sob outra visualização ou fora dos limites da superview, certificando-se de que esteja configurado para cortar seu conteúdo.

Para adicionar a resposta e a adição , criei um UIView simples e reutilizável no Swift. Dependendo do seu caso de uso, você pode querer fazer modificações (evite criar objects em todos os layouts etc.), mas eu queria mantê-lo o mais simples possível. A extensão permite que você aplique isso a outras visualizações (ex. UIImageView ) mais facilmente se você não gosta de subsorting.

 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) } } 

Veja como você aplicaria isso a um 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) } } 

Resumindo a resposta de Stuart, você pode ter o método de canto de arredondamento da seguinte forma:

 @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 

Então, para aplicar o canto de arredondamento, você simplesmente faz:

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