swiftuiimagetransparentuibezierpathuigraphicsimagerenderer

How to make an ellipse/circular UIImage with transparent background?


This is the code I am using

extension UIImage {        
    var ellipseMasked: UIImage? {
        guard let cgImage = cgImage else { return nil }
        let rect = CGRect(origin: .zero, size: size)

        return UIGraphicsImageRenderer(size: size, format: imageRendererFormat)
            .image{ _ in
                UIBezierPath(ovalIn: rect).addClip()
                UIImage(cgImage: cgImage, scale: scale, orientation: imageOrientation)
            .draw(in: rect)
        }
    }
}

This is the image I got

The background color is black. How can I make the background transparent? I tried different ways but haven't made it work yet.


Solution

  • You can subclass UIImageView and mask its CALayer instead of clipping the image itself:

    extension CAShapeLayer {
        convenience init(path: UIBezierPath) {
            self.init()
            self.path = path.cgPath
        }
    }
    

    class EllipsedView: UIImageView {
        override func layoutSubviews() {
            super.layoutSubviews()
            layer.mask = CAShapeLayer(path: .init(ovalIn: bounds))
        }
    }
    

    let profilePicture = UIImage(data: try! Data(contentsOf: URL(string:"https://i.sstatic.net/Xs4RX.jpg")!))!
    let iv = EllipsedView(image: profilePicture)
    

    edit/update

    If you need to clip the UIImage itself you can do it as follow:

    extension UIImage {
        var ellipseMasked: UIImage? {
            UIGraphicsBeginImageContextWithOptions(size, false, scale)
            defer { UIGraphicsEndImageContext() }
            UIBezierPath(ovalIn: .init(origin: .zero, size: size)).addClip()
            draw(in: .init(origin: .zero, size: size))
            return UIGraphicsGetImageFromCurrentImageContext()
        }
    }
    

    For iOS10+ you can use UIGraphicsImageRenderer.

    extension UIImage {
        var ellipseMasked: UIImage {
            let rect = CGRect(origin: .zero, size: size)
            let format = imageRendererFormat
            format.opaque = false
            return UIGraphicsImageRenderer(size: size, format: format).image{ _ in
                UIBezierPath(ovalIn: rect).addClip()
                draw(in: rect)
            }
        }
    }
    

    let profilePicture = UIImage(data: try! Data(contentsOf: URL(string:"https://i.sstatic.net/Xs4RX.jpg")!))!
    profilePicture.ellipseMasked
    

    enter image description here