2014-07-15 5 views
7

Sto provando ad aggiungere una maschera esagonale a una maschera UII in cui sono riuscito. Tuttavia non riesco a arrotondare i lati della maschera esagonale. Ho pensato di aggiungere cell.profilePic.layer.cornerRadius = 10; farebbe il trucco ma non è così.Come arrotondare gli angoli di UIImmagine con maschera esagonale

Ecco il mio codice:

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

Tutte le idee?

Grazie

+0

C'è un po 'di matematica utili e dettagliate su arrotondare gli angoli di un poligono qui: http://stackoverflow.com/questions/20442203/uibezierpath-triangle-with-rounded-edges –

risposta

50

È possibile definire un percorso che è un esagono con angoli arrotondati (che definiscono tale percorso manualmente) e successivamente applicare che la maschera e bordo sottolivello:

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

Quando il percorso di un poligono regolare con angoli arrotondati potrebbero essere implementate in una categoria in questo modo:

@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 (e.g. 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 

E

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

che produce qualcosa in questo modo:

landscape with rounded hexagon border

O a Swift 3 si potrebbe fare:

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 (e.g. 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 

     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 
    } 

} 

Per Swift 2 rendition, vedere previous revision of this answer.

+0

Questo è bello, anche se è ottagonale (8gon) – Desdenova

+0

Lol. La routine del poligono è generalizzata, quindi puoi fornire qualsiasi numero di lati che desideri. Ho aggiornato il campione di conseguenza. – Rob

+2

Non stavo facendo un commento negativo e non ho nemmeno visto il parametro sui lati. Questa è un'ottima risposta. – Desdenova

-1

Forse hai per arrotondare gli angoli del confine e la maschera, piuttosto che della vista dell'immagine?

hexagonMask.cornerRadius = hexagonBorder.cornerRadius = 10.0; 
+1

Questo non funzionerà. Arrotonda solo gli angoli quadrati del livello come descritto dalla sua proprietà 'frame'. È necessario arrotondare gli angoli reali dell'esagono. –

3

Ecco una conversione del metodo RoundPolygon per 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 
} 
+0

e come usarlo, questa funzione? –

+0

Proprio come descritto sopra, crei il tuo UIBezierPath usando questa funzione. – Snwspeckle

3

Ecco la versione 3 rapida di Rob's answer.

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() 
    } 
} 
+0

lo implemento ma questo codice riduce la dimensione dell'immagine. Non mostrare le dimensioni effettive dell'immagine. Come gestirlo? –