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

2

1

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.

Wilmer answered 18/1, 2020 at 21:32 Comment(0)
B
3

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

Bhatt answered 18/1, 2020 at 22:6 Comment(7)
Thanks for the answer! However, what I want is an UIImage, not an UIImageView. That UIImage may be passed to other users later. – Wilmer
@LeoDabus Thank you very much! πŸ‘πŸ‘πŸ‘ – Wilmer
@LeoDabus Thank you very much! It works like a charming! I directly set imageRendererFormat.opaque = false, it didn't work. Can you also explain why it works when you use a temporary variable? – Wilmer
Check imageRendererFormat declaration var imageRendererFormat: UIGraphicsImageRendererFormat { get } There is no set. It is immutable. You need to create an instance of it, change its property and pass it to UIGraphicsImageRenderer initializer – Bhatt
Before you ask me about declaring format as immutable. UIGraphicsImageRendererFormat is a Class. You don't need to declare it as variable to change its properties if they are mutable. You can check opaque declaration as well var opaque: Bool { get set }. – Bhatt
@LeoDabus Thanks for the explanation! You saved my day! – Wilmer
I just added a new feature to one of my projects with the help of this code. Thanks again! @LeoDabus github.com/guoyingtao/Mantis/#credits – Wilmer
T
1

Here are two solutions using SwiftUI.

This solution can be used to clip the image shape to a circle.

Image("imagename").resizable()
    .clipShape(Circle())
    .scaledToFit()

This solution can be used to get more of an eclipse or oval shape from the image.

Image("imagename").resizable()
    .cornerRadius(100)
    .scaledToFit()
    .padding()
Tweed answered 19/7, 2022 at 20:50 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.