Currently i am using the following swift code for scaling cropping and making a circular image out of the passed UIImage . Everything is working until iOS 18 but after that i can observe crashes . I found that from iOS 18 onwards the following three methods have been deprecated;
UIGraphics.BeginImageContextWithOptions(size, false, ScreenDensity);
var image = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
From other SO posts it is recommended to use the you would use UIGraphicsImageRenderer
and its associated functions. Here is the full code using the above deprecated APIs.
func scaleCropAndCircleImage(image:UIImage, frame:CGRect) throws -> UIImage {
enum FunctionError: Error {
case nilContext
case nilImage
}
UIGraphicsBeginImageContextWithOptions(CGSizeMake(frame.size.width, frame.size.height), false, 0.0)
guard let context = UIGraphicsGetCurrentContext() else { throw FunctionError.nilContext }
// width and heights of passed values of uiimage and CGRect
let passedimageWidth = image.size.width
let passedimageHeight = image.size.height
let passedrectWidth = frame.size.width
let passedrectHeight = frame.size.height
// scale factor calculation
let scaleFactorX = passedrectWidth/passedimageWidth
let scaleFactorY = passedrectHeight/passedimageHeight
// centre of the circle calculation
let imageCentreX = passedrectWidth/2
let imageCentreY = passedrectHeight/2
// Creating and clipping to a CIRCULAR Path
let radius = passedrectWidth/2
context.beginPath()
context.addArc(center: CGPoint(x: imageCentreX, y: imageCentreY), radius: radius, startAngle: 0, endAngle: 2 * .pi, clockwise: false)
context.closePath()
context.clip()
//Seting the scaling factor for graphics context
context.scaleBy(x: scaleFactorX, y: scaleFactorY)
let myRect:CGRect = CGRectMake(0, 0, passedimageWidth, passedimageHeight)
image.draw(in: myRect)
let newCroppedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let newCroppedImage else { throw FunctionError.nilImage }
return newCroppedImage
}
Now i am trying to replace the deprecated functions by using the UIGraphicsImageRenderer
but i am stuck in the mid way how to convert the already used logic for the UIGraphicsImageRenderer
functions . Below is the code i have tried so far. Can someone help me in this so that the below function can accept the UIImage and crop it into a circle as done in existing code?
import UIKit
extension UIImage {
func roundedCornerImage(with radius: CGFloat) -> UIImage {
let format = UIGraphicsImageRendererFormat()
format.scale = scale
let renderer = UIGraphicsImageRenderer(size: size, format: format)
return renderer.image { rendererContext in
let rect = CGRect(origin: .zero, size: size)
let path = UIBezierPath(roundedRect: rect,
byRoundingCorners: .allCorners,
cornerRadii: CGSize(width: radius, height: radius))
path.close()
let cgContext = rendererContext.cgContext
cgContext.saveGState()
path.addClip()
draw(in: rect)
cgContext.restoreGState()
}
}
}
If you are just clipping a circle there is no need to calculate any corner size. Just use an eclipse to clip your image.
extension UIImage {
func scaleCropAndCircleImage(to length: CGFloat) -> UIImage {
let format = UIGraphicsImageRendererFormat()
format.scale = scale
let size = CGSize(width: length, height: length)
return UIGraphicsImageRenderer(
size: size,
format: format
).image {
let rect: CGRect = .init(origin: .zero, size: size)
$0.cgContext.addEllipse(in: rect)
$0.cgContext.clip()
draw(in: rect)
}
}
}
Playground test
Task {
do {
let url = URL(string: "https://i.sstatic.net/Xs4RX.jpg")!
let data = try await URLSession.shared.data(from: url).0
if let image = UIImage(data: data) {
let result = image.scaleCropAndCircleImage(to: 100)
// use the resulting image here
}
} catch {
print(error)
}
}
Note that this assumes you are using a squared image. If your images might not be squared you would need to check if it is portrait or landscape and crop what's exceeding horizontally or vertically similar to what's been done in here Cut a UIImage into a circle:
extension UIImage {
var isPortrait: Bool { size.height > size.width }
var isLandscape: Bool { size.width > size.height }
var breadth: CGFloat { min(size.width, size.height) }
var breadthSize: CGSize { .init(width: breadth, height: breadth) }
var breadthRect: CGRect { .init(origin: .zero, size: breadthSize) }
func scaledAndCircleCroped(to length: CGFloat) -> UIImage? {
guard let squared = cgImage?.cropping(
to: .init(
origin: .init(
x: isLandscape ? ((size.width-size.height)/2).rounded(.down) : 0,
y: isPortrait ? ((size.height-size.width)/2).rounded(.down) : 0),
size: breadthSize
)
) else { return nil }
let format = imageRendererFormat
format.opaque = false
let squaredImage = UIImage(
cgImage: squared,
scale: format.scale,
orientation: imageOrientation
)
let size: CGSize = .init(width: length, height: length)
return UIGraphicsImageRenderer(size: size, format: format)
.image {
let rect: CGRect = .init(origin: .zero, size: size)
$0.cgContext.addEllipse(in: rect)
$0.cgContext.clip()
squaredImage.draw(in: rect)
}
}
}
Usage:
Task {
do {
let url = URL(string: "https://i.sstatic.net/Xs4RX.jpg")!
let data = try await URLSession.shared.data(from: url).0
if let result = UIImage(data: data)?.scaledAndCircleCroped(to: 100) {
// use your resulting image here
}
} catch {
print(error)
}
}