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