Sapan Diwakar

Software developer

Follow me on Twitter Check out my code on GitHub View some of my designs on Dribbble Take a look at my Linked In profile

Make Hexagonal View on iOS

In this post, we will explore a bit on how to use CALayer. With CALayers you can create some neat visual effects really easily. Let's see an example on how to mask a view to a hexagon using CALayer. We mostly see square or circular images on most apps. This example is taken from a recent project I worked on which required setting up a hexagonal concept for the images.

internal static func setupHexagonImageView(imageView: UIImageView) {  
    let lineWidth: CGFloat = 5
    let path = Utils.roundedPolygonPath(imageView.bounds, lineWidth: lineWidth, sides: 6, cornerRadius: 10, rotationOffset: CGFloat(M_PI / 2.0))

    let mask = CAShapeLayer()
    mask.path = path.CGPath
    mask.lineWidth = lineWidth
    mask.strokeColor = UIColor.clearColor().CGColor
    mask.fillColor = UIColor.whiteColor().CGColor
    imageView.layer.mask = mask

    let border = CAShapeLayer()
    border.path = path.CGPath
    border.lineWidth = lineWidth
    border.strokeColor = UIColor.whiteColor().CGColor
    border.fillColor = UIColor.clearColor().CGColor
    imageView.layer.addSublayer(border)
}

We just create a CAShapeLayer and add it to the view we want to make hexagonal. The magic however is done in the function below that creates a Hexagonal UIBezierPath for use on the mask.

internal static func roundedPolygonPath(rect: CGRect, lineWidth: CGFloat, sides: NSInteger, cornerRadius: CGFloat, rotationOffset: CGFloat = 0)  
 -> UIBezierPath {
    let path = UIBezierPath()
    let theta: CGFloat = CGFloat(2.0 * M_PI) / CGFloat(sides) // How much to turn at every corner
    let offset: CGFloat = cornerRadius * tan(theta / 2.0)     // Offset from which to start rounding corners
    let width = min(rect.size.width, rect.size.height)        // Width of the square

    let center = CGPoint(x: rect.origin.x + width / 2.0, y: rect.origin.y + width / 2.0)

    // Radius of the circle that encircles the polygon
    // Notice that the radius is adjusted for the corners, that way the largest outer
    // dimension of the resulting shape is always exactly the width - linewidth
    let radius = (width - lineWidth + cornerRadius - (cos(theta) * cornerRadius)) / 2.0

    // Start drawing at a point, which by default is at the right hand edge
    // but can be offset
    var angle = CGFloat(rotationOffset)

    let corner = CGPointMake(center.x + (radius - cornerRadius) * cos(angle), center.y + (radius - cornerRadius) * sin(angle))
    path.moveToPoint(CGPointMake(corner.x + cornerRadius * cos(angle + theta), corner.y + cornerRadius * sin(angle + theta)))

    for _ in 0 ..< sides {
        angle += theta

        let corner = CGPointMake(center.x + (radius - cornerRadius) * cos(angle), center.y + (radius - cornerRadius) * sin(angle))
        let tip = CGPointMake(center.x + radius * cos(angle), center.y + radius * sin(angle))
        let start = CGPointMake(corner.x + cornerRadius * cos(angle - theta), corner.y + cornerRadius * sin(angle - theta))
        let end = CGPointMake(corner.x + cornerRadius * cos(angle + theta), corner.y + cornerRadius * sin(angle + theta))

        path.addLineToPoint(start)
        path.addQuadCurveToPoint(end, controlPoint: tip)
    }

    path.closePath()

    // Move the path to the correct origins
    let bounds = path.bounds
    let transform = CGAffineTransformMakeTranslation(-bounds.origin.x + rect.origin.x + lineWidth / 2.0, 
                      -bounds.origin.y + rect.origin.y + lineWidth / 2.0)
    path.applyTransform(transform)

    return path
}

This gives a really neat effect like this

Hexagonal Image View