Search code examples
iosswiftcore-graphicsuibezierpathcashapelayer

How to add a custom shape to an UIImageView in Swift?


I'm trying to add a custom shape to an imageView. Please check the below images.

This is the required one:

Required Image Shape

This is what I have done so far:

Current Image Shape

I'm new to Core Graphics and I have done this so far:

    private func customImageClipper(imageV: UIImageView){

    let path = UIBezierPath()

    let size = imageV.frame.size

    print(size)

    path.move(to: CGPoint(x: 0.0, y: size.height))

    path.addLine(to: CGPoint(x: 0.8, y: size.height/2))

    path.close()

    let shape = CAShapeLayer()

    shape.path = path.cgPath

    imageV.layer.sublayers = [shape]

}

I'm creating a function to achieve a shape like this, but whenever I pass the imageView into this function, I can not see any change at all. I know that I have to move from points to another point to achieve this shape, but I have never done this. Any help would be appreciated. This is how I'm calling this function:

imageV.layoutIfNeeded()

customImageClipper(imageV: imageV)

P.S.: I'm not using Storyboard, I have created this programmatically.


Solution

  • There are many ways to create shapes using UIBezierPaths. This post here discusses the use of the draw function to create a shape.

    Here is an example using your clip function within the cell.

    func clip(imageView: UIView, withOffset offset: CGFloat) {
        let path = UIBezierPath()
    
        //Move to Top Left
        path.move(to: .init(x: imageView.bounds.size.width * offset, y: 0))
    
        //Draw line from Top Left to Top Right
        path.addLine(to: .init(x: imageView.bounds.size.width, y: 0))
    
        //Draw Line from Top Right to Bottom Right
        path.addLine(to: .init(x: imageView.bounds.size.width * (1 - offset), y: imageView.bounds.size.height))
    
        //Draw Line from Bottom Right to Bottom Left
        path.addLine(to: .init(x: 0, y: imageView.bounds.size.height))
    
        //Close Path
        path.close()
    
        //Create the Shape Mask for the ImageView
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = path.cgPath
        shapeLayer.fillColor = UIColor.black.cgColor
        imageView.layer.mask = shapeLayer
    }
    

    In this function, the offset is the amount of angle you would like on the shape, ranging from 0 to 1. (0.4) seems to work for your requirements.

    This shares a lot of similarities with Apseri's answer, except I chose the route of percentages, rather than exact size. Nothing wrong with either approach, I just found it easier to understand with percentages. :)

    One last note to point out, I used this function in the layoutSubviews function.

    override func layoutSubviews() {
        super.layoutSubviews()
        imageView.layoutIfNeeded()
        clip(imageView: self.imageView, withOffset: 0.4)
    }
    

    This output the following image:

    Image of a cell showing custom shape

    Hope this helps.