Cómo redondear las esquinas de UIImage con la máscara Hexagon

Intento agregar una máscara hexagonal a un UIImage en el que he tenido éxito. Sin embargo, no puedo redondear los lados de la máscara hexagonal. Pensé en agregar cell.profilePic.layer.cornerRadius = 10; haría el truco, pero no es así.

Aquí está mi código:

CGRect rect = cell.profilePic.frame; CAShapeLayer *hexagonMask = [CAShapeLayer layer]; CAShapeLayer *hexagonBorder = [CAShapeLayer layer]; hexagonBorder.frame = cell.profilePic.layer.bounds; UIBezierPath *hexagonPath = [UIBezierPath bezierPath]; CGFloat sideWidth = 2 * ( 0.5 * rect.size.width / 2 ); CGFloat lcolumn = ( rect.size.width - sideWidth ) / 2; CGFloat rcolumn = rect.size.width - lcolumn; CGFloat height = 0.866025 * rect.size.height; CGFloat y = (rect.size.height - height) / 2; CGFloat by = rect.size.height - y; CGFloat midy = rect.size.height / 2; CGFloat rightmost = rect.size.width; [hexagonPath moveToPoint:CGPointMake(lcolumn, y)]; [hexagonPath addLineToPoint:CGPointMake(rcolumn, y)]; [hexagonPath addLineToPoint:CGPointMake(rightmost, midy)]; [hexagonPath addLineToPoint:CGPointMake(rcolumn, by)]; [hexagonPath addLineToPoint:CGPointMake(lcolumn, by)]; [hexagonPath addLineToPoint:CGPointMake(0, midy)]; [hexagonPath addLineToPoint:CGPointMake(lcolumn, y)]; hexagonMask.path = hexagonPath.CGPath; hexagonBorder.path = hexagonPath.CGPath; hexagonBorder.fillColor = [UIColor clearColor].CGColor; hexagonBorder.strokeColor = [UIColor blackColor].CGColor; hexagonBorder.lineWidth = 5; cell.profilePic.layer.mask = hexagonMask; cell.profilePic.layer.cornerRadius = 10; cell.profilePic.layer.masksToBounds = YES; [cell.profilePic.layer addSublayer:hexagonBorder]; 

¿Algunas ideas?

Gracias

Puede definir una ruta que sea un hexágono con esquinas redondeadas (definir esa ruta manualmente) y luego aplicarla como la máscara y la subcapa de borde:

 CGFloat lineWidth = 5.0; UIBezierPath *path = [UIBezierPath polygonInRect:self.imageView.bounds sides:6 lineWidth:lineWidth cornerRadius:30]; // mask for the image view CAShapeLayer *mask = [CAShapeLayer layer]; mask.path = path.CGPath; mask.lineWidth = lineWidth; mask.strokeColor = [UIColor clearColor].CGColor; mask.fillColor = [UIColor whiteColor].CGColor; self.imageView.layer.mask = mask; // if you also want a border, add that as a separate layer CAShapeLayer *border = [CAShapeLayer layer]; border.path = path.CGPath; border.lineWidth = lineWidth; border.strokeColor = [UIColor blackColor].CGColor; border.fillColor = [UIColor clearColor].CGColor; [self.imageView.layer addSublayer:border]; 

Donde la ruta de un polígono regular con esquinas redondeadas podría implementarse en una categoría como esta:

 @interface UIBezierPath (Polygon) /** Create UIBezierPath for regular polygon with rounded corners * * @param rect The CGRect of the square in which the path should be created. * @param sides How many sides to the polygon (eg 6=hexagon; 8=octagon, etc.). * @param lineWidth The width of the stroke around the polygon. The polygon will be inset such that the stroke stays within the above square. * @param cornerRadius The radius to be applied when rounding the corners. * * @return UIBezierPath of the resulting rounded polygon path. */ + (instancetype)polygonInRect:(CGRect)rect sides:(NSInteger)sides lineWidth:(CGFloat)lineWidth cornerRadius:(CGFloat)cornerRadius; @end 

Y

 @implementation UIBezierPath (Polygon) + (instancetype)polygonInRect:(CGRect)rect sides:(NSInteger)sides lineWidth:(CGFloat)lineWidth cornerRadius:(CGFloat)cornerRadius { UIBezierPath *path = [UIBezierPath bezierPath]; CGFloat theta = 2.0 * M_PI / sides; // how much to turn at every corner CGFloat offset = cornerRadius * tanf(theta / 2.0); // offset from which to start rounding corners CGFloat squareWidth = MIN(rect.size.width, rect.size.height); // width of the square // calculate the length of the sides of the polygon CGFloat length = squareWidth - lineWidth; if (sides % 4 != 0) { // if not dealing with polygon which will be square with all sides ... length = length * cosf(theta / 2.0) + offset/2.0; // ... offset it inside a circle inside the square } CGFloat sideLength = length * tanf(theta / 2.0); // start drawing at `point` in lower right corner CGPoint point = CGPointMake(rect.origin.x + rect.size.width / 2.0 + sideLength / 2.0 - offset, rect.origin.y + rect.size.height / 2.0 + length / 2.0); CGFloat angle = M_PI; [path moveToPoint:point]; // draw the sides and rounded corners of the polygon for (NSInteger side = 0; side < sides; side++) { point = CGPointMake(point.x + (sideLength - offset * 2.0) * cosf(angle), point.y + (sideLength - offset * 2.0) * sinf(angle)); [path addLineToPoint:point]; CGPoint center = CGPointMake(point.x + cornerRadius * cosf(angle + M_PI_2), point.y + cornerRadius * sinf(angle + M_PI_2)); [path addArcWithCenter:center radius:cornerRadius startAngle:angle - M_PI_2 endAngle:angle + theta - M_PI_2 clockwise:YES]; point = path.currentPoint; // we don't have to calculate where the arc ended ... UIBezierPath did that for us angle += theta; } [path closePath]; path.lineWidth = lineWidth; // in case we're going to use CoreGraphics to stroke path, rather than CAShapeLayer path.lineJoinStyle = kCGLineJoinRound; return path; } 

Eso produce algo así:

paisaje con borde hexagonal redondeado

O en Swift 3 podrías hacer:

 let lineWidth: CGFloat = 5 let path = UIBezierPath(polygonIn: imageView.bounds, sides: 6, lineWidth: lineWidth, cornerRadius: 30) let mask = CAShapeLayer() mask.path = path.cgPath mask.lineWidth = lineWidth mask.strokeColor = UIColor.clear.cgColor mask.fillColor = UIColor.white.cgColor imageView.layer.mask = mask let border = CAShapeLayer() border.path = path.cgPath border.lineWidth = lineWidth border.strokeColor = UIColor.black.cgColor border.fillColor = UIColor.clear.cgColor imageView.layer.addSublayer(border) 

Con

 extension UIBezierPath { /// Create UIBezierPath for regular polygon with rounded corners /// /// - parameter rect: The CGRect of the square in which the path should be created. /// - parameter sides: How many sides to the polygon (eg 6=hexagon; 8=octagon, etc.). /// - parameter lineWidth: The width of the stroke around the polygon. The polygon will be inset such that the stroke stays within the above square. Default value 1. /// - parameter cornerRadius: The radius to be applied when rounding the corners. Default value 0. convenience init(polygonIn rect: CGRect, sides: Int, lineWidth: CGFloat = 1, cornerRadius: CGFloat = 0) { self.init() let theta = 2 * CGFloat.pi / CGFloat(sides) // how much to turn at every corner let offset = cornerRadius * tan(theta / 2) // offset from which to start rounding corners let squareWidth = min(rect.size.width, rect.size.height) // width of the square // calculate the length of the sides of the polygon var length = squareWidth - lineWidth if sides % 4 != 0 { // if not dealing with polygon which will be square with all sides ... length = length * cos(theta / 2) + offset / 2 // ... offset it inside a circle inside the square } let sideLength = length * tan(theta / 2) // if you'd like to start rotated 90 degrees, use these lines instead of the following two: // // var point = CGPoint(x: rect.midX - length / 2, y: rect.midY + sideLength / 2 - offset) // var angle = -CGFloat.pi / 2.0 // if you'd like to start rotated 180 degrees, use these lines instead of the following two: // // var point = CGPoint(x: rect.midX - sideLength / 2 + offset, y: rect.midY - length / 2) // var angle = CGFloat(0) var point = CGPoint(x: rect.midX + sideLength / 2 - offset, y: rect.midY + length / 2) var angle = CGFloat.pi move(to: point) // draw the sides and rounded corners of the polygon for _ in 0 ..< sides { point = CGPoint(x: point.x + (sideLength - offset * 2) * cos(angle), y: point.y + (sideLength - offset * 2) * sin(angle)) addLine(to: point) let center = CGPoint(x: point.x + cornerRadius * cos(angle + .pi / 2), y: point.y + cornerRadius * sin(angle + .pi / 2)) addArc(withCenter: center, radius: cornerRadius, startAngle: angle - .pi / 2, endAngle: angle + theta - .pi / 2, clockwise: true) point = currentPoint angle += theta } close() self.lineWidth = lineWidth // in case we're going to use CoreGraphics to stroke path, rather than CAShapeLayer lineJoinStyle = .round } } 

Para la versión Swift 2, ver la revisión anterior de esta respuesta .

Aquí hay una conversión del método del polígono redondeado para Swift.

 func roundedPolygonPathWithRect(square: CGRect, lineWidth: Float, sides: Int, cornerRadius: Float) -> UIBezierPath { var path = UIBezierPath() let theta = Float(2.0 * M_PI) / Float(sides) let offset = cornerRadius * tanf(theta / 2.0) let squareWidth = Float(min(square.size.width, square.size.height)) var length = squareWidth - lineWidth if sides % 4 != 0 { length = length * cosf(theta / 2.0) + offset / 2.0 } var sideLength = length * tanf(theta / 2.0) var point = CGPointMake(CGFloat((squareWidth / 2.0) + (sideLength / 2.0) - offset), CGFloat(squareWidth - (squareWidth - length) / 2.0)) var angle = Float(M_PI) path.moveToPoint(point) for var side = 0; side < sides; side++ { let x = Float(point.x) + (sideLength - offset * 2.0) * cosf(angle) let y = Float(point.y) + (sideLength - offset * 2.0) * sinf(angle) point = CGPointMake(CGFloat(x), CGFloat(y)) path.addLineToPoint(point) let centerX = Float(point.x) + cornerRadius * cosf(angle + Float(M_PI_2)) let centerY = Float(point.y) + cornerRadius * sinf(angle + Float(M_PI_2)) var center = CGPointMake(CGFloat(centerX), CGFloat(centerY)) let startAngle = CGFloat(angle) - CGFloat(M_PI_2) let endAngle = CGFloat(angle) + CGFloat(theta) - CGFloat(M_PI_2) path.addArcWithCenter(center, radius: CGFloat(cornerRadius), startAngle: startAngle, endAngle: endAngle, clockwise: true) point = path.currentPoint angle += theta } path.closePath() return path } 

Aquí está la versión rápida 3 de la respuesta de Rob .

  let lineWidth: CGFloat = 5.0 let path = UIBezierPath(roundedPolygonPathWithRect: self.bounds, lineWidth: lineWidth, sides: 6, cornerRadius: 12) let mask = CAShapeLayer() mask.path = path.cgPath mask.lineWidth = lineWidth mask.strokeColor = UIColor.clear.cgColor mask.fillColor = UIColor.white.cgColor self.layer.mask = mask let border = CAShapeLayer() border.path = path.cgPath border.lineWidth = lineWidth border.strokeColor = UIColor.black.cgColor border.fillColor = UIColor.clear.cgColor self.layer.addSublayer(border) extension UIBezierPath { convenience init(roundedPolygonPathWithRect rect: CGRect, lineWidth: CGFloat, sides: NSInteger, cornerRadius: CGFloat) { self.init() let theta = CGFloat(2.0 * M_PI) / CGFloat(sides) let offSet = CGFloat(cornerRadius) / CGFloat(tan(theta/2.0)) let squareWidth = min(rect.size.width, rect.size.height) var length = squareWidth - lineWidth if sides%4 != 0 { length = length * CGFloat(cos(theta / 2.0)) + offSet/2.0 } let sideLength = length * CGFloat(tan(theta / 2.0)) var point = CGPoint(x: squareWidth / 2.0 + sideLength / 2.0 - offSet, y: squareWidth - (squareWidth - length) / 2.0) var angle = CGFloat(M_PI) move(to: point) for _ in 0 ..< sides { point = CGPoint(x: point.x + CGFloat(sideLength - offSet * 2.0) * CGFloat(cos(angle)), y: point.y + CGFloat(sideLength - offSet * 2.0) * CGFloat(sin(angle))) addLine(to: point) let center = CGPoint(x: point.x + cornerRadius * CGFloat(cos(angle + CGFloat(M_PI_2))), y: point.y + cornerRadius * CGFloat(sin(angle + CGFloat(M_PI_2)))) addArc(withCenter: center, radius:CGFloat(cornerRadius), startAngle:angle - CGFloat(M_PI_2), endAngle:angle + theta - CGFloat(M_PI_2), clockwise:true) point = currentPoint // we don't have to calculate where the arc ended ... UIBezierPath did that for us angle += theta } close() } } 

Tal vez tienes que redondear las esquinas del borde y la máscara, en lugar de la vista de la imagen.

 hexagonMask.cornerRadius = hexagonBorder.cornerRadius = 10.0;