How to round corners of UIImage with Hexagon mask

前端 未结 4 1496
轻奢々
轻奢々 2020-11-28 10:21

I\'m trying to add a hexagon mask to a UIImage which I has been successful in. However I am unable to round the sides of the hexagon mask. I thought adding cell.profilePic.l

相关标签:
4条回答
  • 2020-11-28 10:40

    Here's a conversion of the roundedPolygon method for 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 讨论(0)
  • 2020-11-28 10:48

    Maybe you have to round the corners of the border and the mask, rather than of the image view?

    hexagonMask.cornerRadius = hexagonBorder.cornerRadius = 10.0;
    
    0 讨论(0)
  • 2020-11-28 10:54

    Here is the swift 3 version of 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 讨论(0)
  • 2020-11-28 10:58

    You can define a path that is a hexagon with rounded corners (defining that path manually) and then apply that as the mask and border sublayer:

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

    Where the path of a regular polygon with rounded corners might be implemented in a category like so:

    @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
    

    And

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

    That yields something like so:

    Or in Swift 3 you might do:

    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)
    

    With

    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 * .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.width, rect.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
        }
    
    }
    

    For Swift 2 rendition, see previous revision of this answer.

    0 讨论(0)
提交回复
热议问题