IOS developer
IOS developer

Reputation: 67

How to set a imageview like a hexagon in ios exactly below

I need to give bounds of an imageView like a hexagon. I've searched in Stack Overflow and get the code, but it not exactly satisfy the need.

Please click here.this is my example imageview

Upvotes: 1

Views: 1928

Answers (2)

Dennis Pashkov
Dennis Pashkov

Reputation: 944

Ok, here is an example of how to do it.

First, create a category on UIBezierPath:

Objective-C:

.h file:

@import UIKit.UIBezierPath;

@interface UIBezierPath (Additions)

+ (UIBezierPath * _Nullable)bezierPathWithSquare:(CGRect)square numberOfSides:(NSUInteger)numberOfSides cornerRadius:(CGFloat)cornerRadius;

@end

.m file:

#import "UIBezierPath+Additions.h"

@implementation UIBezierPath (Additions)

+ (UIBezierPath *)bezierPathWithSquare:(CGRect)square numberOfSides:(NSUInteger)numberOfSides cornerRadius:(CGFloat)cornerRadius
{
    if ( CGRectGetWidth(square) != CGRectGetHeight(square) )
    {
        return nil;
    }

    CGFloat squareWidth = CGRectGetWidth(square);

    if ( numberOfSides == 0 || cornerRadius < 0.0 || 2.0 * cornerRadius > squareWidth || CGRectIsInfinite(square) || CGRectIsEmpty(square) || CGRectIsNull(square) )
    {
        return nil;
    }

    UIBezierPath *path = [UIBezierPath new];

    CGFloat theta = 2.0 * M_PI / numberOfSides;
    CGFloat offset = cornerRadius * tan(0.5 * theta);

    CGFloat length = squareWidth - path.lineWidth;
    if ( numberOfSides % 4 != 0 )
    {
        length = length * cos(0.5 * theta);
    }

    CGFloat sideLength = length * tan(0.5 * theta);

    CGFloat p1 = squareWidth / 2.0 + sideLength / 2.0 - offset;
    CGFloat p2 = squareWidth - (squareWidth - length) / 2.0;
    CGPoint point = CGPointMake(p1, p2);
    CGFloat angle = M_PI;
    [path moveToPoint:point];

    for ( NSUInteger i = 0; i < numberOfSides; i++ )
    {
        CGFloat x1 = point.x + ( sideLength - offset * 2.0 ) * cos(angle);
        CGFloat y1 = point.y + ( sideLength - offset * 2.0 ) * sin(angle);

        point = CGPointMake(x1, y1);
        [path addLineToPoint:point];

        CGFloat centerX = point.x + cornerRadius * cos(angle + M_PI_2);
        CGFloat centerY = point.y + cornerRadius * sin(angle + M_PI_2);
        CGPoint center = CGPointMake(centerX, centerY);
        CGFloat radius = cornerRadius;
        CGFloat startAngle = angle - M_PI_2;
        CGFloat endAngle = angle + theta - M_PI_2;

        [path addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
        point = path.currentPoint;
        angle += theta;
    }

    [path closePath];

    return path;
}

@end

Then, your view controller's implementation will look like this:

#import "ViewController.h"
#import "UIBezierPath+Additions.h"

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIImageView *imageView;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self setupImageViewHexagonMask];
}

- (void)setupImageViewHexagonMask
{
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithSquare:self.imageView.bounds numberOfSides:6 cornerRadius:20.0];
    CAShapeLayer *maskingLayer = [CAShapeLayer layer];
    maskingLayer.path = maskPath.CGPath;

    self.imageView.layer.mask = maskingLayer;
}

@end

Swift 2:

Create an extension on UIBezierPath

import UIKit

/** Additions Extends UIBezierPath

*/
extension UIBezierPath {

    convenience init?(square: CGRect, numberOfSides: UInt, cornerRadius: CGFloat) {

        guard CGRectGetWidth(square) == CGRectGetHeight(square) else {

            return nil
        }

        let squareWidth = CGRectGetWidth(square)

        guard numberOfSides > 0 && cornerRadius >= 0.0 && 2.0 * cornerRadius < squareWidth && !CGRectIsInfinite(square) && !CGRectIsEmpty(square) && !CGRectIsNull(square) else {

            return nil
        }

        self.init()

        // how much to turn at every corner
        let theta =  CGFloat(2.0 * M_PI) / CGFloat(numberOfSides)

        // offset from which to start rounding corners
        let offset: CGFloat = cornerRadius * CGFloat(tan( theta / 2.0 ))

        var length = squareWidth - lineWidth
        if( numberOfSides % 4 != 0){
            length = length * cos(theta / 2.0)
        }

        let sideLength = length * CGFloat(tan(theta / 2.0))

        // start drawing ap 'point' in lower right corner
        let p1 = squareWidth / 2.0 + sideLength / 2.0 - offset
        let p2 = squareWidth - ((squareWidth - length) / 2.0)
        var point = CGPointMake(CGFloat(p1), CGFloat(p2))
        var angle = CGFloat(M_PI)
        moveToPoint(point)

        // draw the sides around rounded corners of the polygon
        for ( var i: UInt = 0; i < numberOfSides; i++){
            let x1 = CGFloat(point.x) + ((sideLength - offset * 2.0) * CGFloat(cos(angle)))
            let y1 = CGFloat(point.y) + ((sideLength - offset * 2.0) * CGFloat(sin(angle)))

            point = CGPointMake(CGFloat(x1), CGFloat(y1))
            addLineToPoint(point)

            let centerX = point.x + cornerRadius * CGFloat(cos(angle + CGFloat(M_PI_2)))
            let centerY = point.y + cornerRadius * CGFloat(sin(angle + CGFloat(M_PI_2)))
            let center = CGPointMake(CGFloat(centerX), CGFloat(centerY))
            let radius:CGFloat = CGFloat(cornerRadius)
            let startAngle = CGFloat(angle) - CGFloat(M_PI_2)
            let endAngle = CGFloat(angle) + CGFloat(theta) - CGFloat(M_PI_2)

            addArcWithCenter(center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
            point = currentPoint
            angle += theta

        }

        closePath()
    }
}

Example view controller's implementation:

import UIKit

class ViewController: UIViewController {

    @IBOutlet private weak var imageView: UIImageView!

    override func viewWillAppear(animated: Bool) {

        super.viewWillAppear(animated)
        applyHexagonMask()
    }

    private func applyHexagonMask() {

        let maskPath = UIBezierPath(square: imageView.bounds, numberOfSides: 6, cornerRadius: 10.0)

        let maskingLayer = CAShapeLayer()
        maskingLayer.path = maskPath?.CGPath

        imageView.layer.mask = maskingLayer
    }
}

Swift 4:

import UIKit

extension UIBezierPath {

    convenience init?(square: CGRect, numberOfSides: UInt, cornerRadius: CGFloat) {

        guard square.width == square.height else { return nil }

        let squareWidth = square.width

        guard numberOfSides > 0 && cornerRadius >= 0.0 && 2.0 * cornerRadius < squareWidth && !square.isInfinite && !square.isEmpty && !square.isNull else {

            return nil
        }

        self.init()

        // how much to turn at every corner
        let theta =  2.0 * .pi / CGFloat(numberOfSides)
        let halfTheta = 0.5 * theta

        // offset from which to start rounding corners
        let offset: CGFloat = cornerRadius * CGFloat(tan(halfTheta))

        var length = squareWidth - self.lineWidth
        if numberOfSides % 4 > 0 {

            length = length * cos(halfTheta)
        }

        let sideLength = length * CGFloat(tan(halfTheta))

        // start drawing at 'point' in lower right corner
        let p1 = 0.5 * (squareWidth + sideLength) - offset
        let p2 = squareWidth - 0.5 * (squareWidth - length)
        var point = CGPoint(x: p1, y: p2)
        var angle = CGFloat.pi

        self.move(to: point)

        // draw the sides around rounded corners of the polygon
        for _ in 0..<numberOfSides {

            let x1 = CGFloat(point.x) + ((sideLength - offset * 2.0) * CGFloat(cos(angle)))
            let y1 = CGFloat(point.y) + ((sideLength - offset * 2.0) * CGFloat(sin(angle)))

            point = CGPoint(x: x1, y: y1)
            self.addLine(to: point)

            let centerX = point.x + cornerRadius * CGFloat(cos(angle + 0.5 * .pi))
            let centerY = point.y + cornerRadius * CGFloat(sin(angle + 0.5 * .pi))
            let center = CGPoint(x: centerX, y: centerY)
            let startAngle = CGFloat(angle) - 0.5 * .pi
            let endAngle = CGFloat(angle) + CGFloat(theta) - 0.5 * .pi

            self.addArc(withCenter: center, radius: cornerRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
            point = self.currentPoint
            angle += theta

        }

        self.close()
    }
}

Example view controller's implementation:

class ViewController: UIViewController {

    @IBOutlet private weak var imageView: UIImageView!

    override func viewWillAppear(_ animated: Bool) {

        super.viewWillAppear(animated)
        applyHexagonMask()
    }

    private func applyHexagonMask() {

        let maskPath = UIBezierPath(square: imageView.bounds, numberOfSides: 6, cornerRadius: 10.0)

        let maskingLayer = CAShapeLayer()
        maskingLayer.path = maskPath?.cgPath

        imageView.layer.mask = maskingLayer
    }
}

Result from playground:

Result

Enjoy.

Upvotes: 5

IOS developer
IOS developer

Reputation: 67

The below code works for me:

CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.fillRule = kCAFillRuleEvenOdd;
maskLayer.frame = img_profilepic.bounds;

CGFloat width = img_profilepic.frame.size.width;
CGFloat height = img_profilepic.frame.size.height;
CGFloat hPadding = width * 1 / 8 / 2;

UIGraphicsBeginImageContext(img_profilepic.frame.size);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(width/2, 0)];
[path addLineToPoint:CGPointMake(width - hPadding, height / 4)];
[path addLineToPoint:CGPointMake(width - hPadding, height * 3 / 4)];
[path addLineToPoint:CGPointMake(width / 2, height)];
[path addLineToPoint:CGPointMake(hPadding, height * 3 / 4)];
[path addLineToPoint:CGPointMake(hPadding, height / 4)];
[path closePath];
[path closePath];
[path fill];
[path stroke];

maskLayer.path = path.CGPath;
UIGraphicsEndImageContext();

img_profilepic.layer.mask = maskLayer;
img_profilepic.image=[UIImage imageNamed:@"profile_picture"];

Upvotes: 0

Related Questions