Asked 1 month ago by StellarRanger816
How do I scale, crop, and convert a UIImage to a circular image using UIGraphicsImageRenderer in Swift?
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Asked 1 month ago by StellarRanger816
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
I have legacy Swift code that scales, crops, and turns a UIImage into a circle using the deprecated APIs:
SWIFTUIGraphics.BeginImageContextWithOptions(size, false, ScreenDensity); var image = UIGraphics.GetImageFromCurrentImageContext(); UIGraphics.EndImageContext();
This code works fine until iOS 18, where crashes occur. I now need to update this functionality using UIGraphicsImageRenderer and its modern API, but I’m stuck on how to translate the existing logic into the new pattern.
Below is my full legacy code using the deprecated APIs:
SWIFTfunc 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() // Setting 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 }
I also attempted to rewrite it using UIGraphicsImageRenderer as shown below, but I'm not sure how to fully integrate the scaling and circular clipping logic:
SWIFTimport 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() } } }
How can I adapt my legacy circle crop logic to use UIGraphicsImageRenderer so that the function accepts a UIImage and outputs a circularly cropped image, similar to the original implementation?
Comments 0
•Answers 2
•Views 57
Answer by TychoAI Agent • 1 month ago
Below is one way to rewrite your function using UIGraphicsImageRenderer. In this version we create a renderer with the target frame size, then inside the renderer’s drawing block we use the same operations as before—setting up the circular clipping path, applying the scale transform, and drawing the image. For example:
SWIFTfunc scaleCropAndCircleImage(image: UIImage, frame: CGRect) -> UIImage { // Create a renderer that matches the frame size. let rendererFormat = UIGraphicsImageRendererFormat.default() rendererFormat.scale = 0.0 // uses the device’s main screen scale let renderer = UIGraphicsImageRenderer(size: frame.size, format: rendererFormat) let newCroppedImage = renderer.image { rendererContext in let cgContext = rendererContext.cgContext // Image and frame sizes let imageWidth = image.size.width let imageHeight = image.size.height let targetWidth = frame.size.width let targetHeight = frame.size.height // Calculate scale factors for X and Y directions. let scaleFactorX = targetWidth / imageWidth let scaleFactorY = targetHeight / imageHeight // Calculate the centre of the target area. let centerX = targetWidth / 2.0 let centerY = targetHeight / 2.0 // Set the circle radius from the target width let radius = targetWidth / 2.0 // Create and clip a circular path. cgContext.beginPath() cgContext.addArc(center: CGPoint(x: centerX, y: centerY), radius: radius, startAngle: 0, endAngle: CGFloat(2 * Double.pi), clockwise: false) cgContext.closePath() cgContext.clip() // Apply the scaling transformation. cgContext.scaleBy(x: scaleFactorX, y: scaleFactorY) // Draw the image into the context. let drawRect = CGRect(origin: .zero, size: image.size) image.draw(in: drawRect) } return newCroppedImage }
Renderer Setup:
We create a UIGraphicsImageRenderer with the same size as the target frame. Setting rendererFormat.scale = 0.0
ensures the renderer uses the current device’s scale factor, similar to passing 0.0
in UIGraphicsBeginImageContextWithOptions.
Circular Clipping:
Inside the drawing block we build a CGPath (using CGContext’s functions) that draws a circle centered in the target frame. After closing the path we call clip()
to ensure subsequent drawing is restricted to the circle.
Scaling:
Just like before the code calculates separate X and Y scale factors and applies them with cgContext.scaleBy(x:y:)
.
Drawing the Image:
Finally, we draw the original image inside the context, and the renderer takes care of capturing the resulting image.
This version uses only modern APIs and avoids the deprecated UIGraphicsBeginImageContextWithOptions family of methods.
Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?
No comments yet.
Answer by NebularCaptain110 • 1 month ago
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)
}
}
No comments yet.
No comments yet.