I need to be able to programmatically draw on an image, and save that image for later use. Say, draw a line on specific x and y coordinates on the image, save the image, and display it onto a simple view controller. How would I go about doing this in Swift? (Preferably Swift 2, I am still in development and haven't updated my mac to Sierra)
Update: Possibly something to do with converting a UIImage to a CGLayer, drawing on it, and then converting it back to a UIImage.

- 391
- 1
- 3
- 11
-
You convert the UIImage to CGContext, not CGLayer. – kennytm Oct 10 '16 at 01:29
5 Answers
All you need to do is create and get an Image Context object and access all its powerful drawing methods. You can learn more about the CGContext
object features here.
This function draws a line and a circle on an UIImage and returns the modified image:
Swift 4
func drawOnImage(_ image: UIImage) -> UIImage {
// Create a context of the starting image size and set it as the current one
UIGraphicsBeginImageContext(image.size)
// Draw the starting image in the current context as background
image.draw(at: CGPoint.zero)
// Get the current context
let context = UIGraphicsGetCurrentContext()!
// Draw a red line
context.setLineWidth(2.0)
context.setStrokeColor(UIColor.red.cgColor)
context.move(to: CGPoint(x: 100, y: 100))
context.addLine(to: CGPoint(x: 200, y: 200))
context.strokePath()
// Draw a transparent green Circle
context.setStrokeColor(UIColor.green.cgColor)
context.setAlpha(0.5)
context.setLineWidth(10.0)
context.addEllipse(in: CGRect(x: 100, y: 100, width: 100, height: 100))
context.drawPath(using: .stroke) // or .fillStroke if need filling
// Save the context as a new UIImage
let myImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// Return modified image
return myImage
}

- 5,396
- 4
- 31
- 54

- 657
- 1
- 7
- 8
It's simple:
Make an image graphics context. (Before iOS 10, you would do this by calling
UIGraphicsBeginImageContextWithOptions
. In iOS 10 there's another way, UIGraphicsImageRenderer, but you don't have to use it if you don't want to.)Draw (i.e. copy) the image into the context. (UIImage actually has
draw...
methods for this very purpose.)Draw your line into the context. (There are CGContext functions for this.)
Extract the resulting image from the context. (For example, if you used
UIGraphicsBeginImageContextWithOptions
, you would useUIGraphicsGetImageFromCurrentImageContext
.) Then close the context.

- 515,959
- 87
- 875
- 1,141
-
1I've written a book that you can read online that teaches you how to do this: http://www.apeth.com/iOSBook/ch15.html#_graphics_contexts – matt Oct 10 '16 at 01:41
-
In Swift 2, it's really all the same, because it's just a bunch of C function calls and Swift just calls them directly. Swift 3 is very different. – matt Oct 10 '16 at 01:49
-
Actually, using UIGraphicsBeginImageContextWithOptions and UIGraphicsGetImageFromCurrentImageContext will cost much more memory rather than using UIGraphicsImageRenderer API. – Chris Forever Apr 24 '20 at 03:01
-
1@ChrisForever I always use image renderer nowadays because it's a better API, but I have no reason to believe there's a memory difference; an image context is an image context, a buffer the size of the image. – matt Apr 24 '20 at 03:14
Details
Xcode 9.1, Swift 4
Solution
extension UIImage
extension UIImage {
typealias RectCalculationClosure = (_ parentSize: CGSize, _ newImageSize: CGSize)->(CGRect)
func with(image named: String, rectCalculation: RectCalculationClosure) -> UIImage {
return with(image: UIImage(named: named), rectCalculation: rectCalculation)
}
func with(image: UIImage?, rectCalculation: RectCalculationClosure) -> UIImage {
if let image = image {
UIGraphicsBeginImageContext(size)
draw(in: CGRect(origin: .zero, size: size))
image.draw(in: rectCalculation(size, image.size))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
return self
}
}
extension UIImageView
extension UIImageView {
enum ImageAddingMode {
case changeOriginalImage
case addSubview
}
func drawOnCurrentImage(anotherImage: UIImage?, mode: ImageAddingMode, rectCalculation: UIImage.RectCalculationClosure) {
guard let image = image else {
return
}
switch mode {
case .changeOriginalImage:
self.image = image.with(image: anotherImage, rectCalculation: rectCalculation)
case .addSubview:
let newImageView = UIImageView(frame: rectCalculation(frame.size, image.size))
newImageView.image = anotherImage
addSubview(newImageView)
}
}
}
Images samples
Parent Image:
Child Image:
Usage example 1
func sample1(imageView: UIImageView) {
imageView.contentMode = .scaleAspectFit
imageView.image = UIImage(named: "parent")?.with(image: "child") { parentSize, newImageSize in
print("parentSize = \(parentSize)")
print("newImageSize = \(newImageSize)")
return CGRect(x: 50, y: 50, width: 90, height: 90)
}
}
Result 1
Usage example 2
func sample2(imageView: UIImageView) {
imageView.contentMode = .scaleAspectFit
imageView.image = UIImage(named: "parent")
imageView.drawOnCurrentImage(anotherImage: UIImage(named: "child"), mode: .changeOriginalImage) { parentSize, newImageSize in
print("parentSize = \(parentSize)")
print("newImageSize = \(newImageSize)")
let sideLength:CGFloat = 90
let indent:CGFloat = 50
return CGRect(x: parentSize.width-sideLength-indent, y: parentSize.height-sideLength-indent, width: sideLength, height: sideLength)
}
}
Result 2
Usage example 3
func sample3(imageView: UIImageView) {
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.image = UIImage(named: "parent")
imageView.drawOnCurrentImage(anotherImage: UIImage(named: "child"), mode: .addSubview) { parentSize, newImageSize in
print("parentSize = \(parentSize)")
print("newImageSize = \(newImageSize)")
let sideLength:CGFloat = 90
let indent:CGFloat = 15
return CGRect(x: parentSize.width-sideLength-indent, y: indent, width: sideLength, height: sideLength)
}
}
Result 3
Full sample code
Don't forget to add Solution code here
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let imageView = UIImageView(frame: UIScreen.main.bounds)
view.addSubview(imageView)
sample1(imageView: imageView)
// sample2(imageView: imageView)
// sample3(imageView: imageView)
}
func sample1(imageView: UIImageView) {
imageView.contentMode = .scaleAspectFit
imageView.image = UIImage(named: "parent")?.with(image: "child") { parentSize, newImageSize in
print("parentSize = \(parentSize)")
print("newImageSize = \(newImageSize)")
return CGRect(x: 50, y: 50, width: 90, height: 90)
}
}
func sample2(imageView: UIImageView) {
imageView.contentMode = .scaleAspectFit
imageView.image = UIImage(named: "parent")
imageView.drawOnCurrentImage(anotherImage: UIImage(named: "child"), mode: .changeOriginalImage) { parentSize, newImageSize in
print("parentSize = \(parentSize)")
print("newImageSize = \(newImageSize)")
let sideLength:CGFloat = 90
let indent:CGFloat = 50
return CGRect(x: parentSize.width-sideLength-indent, y: parentSize.height-sideLength-indent, width: sideLength, height: sideLength)
}
}
func sample3(imageView: UIImageView) {
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.image = UIImage(named: "parent")
imageView.drawOnCurrentImage(anotherImage: UIImage(named: "child"), mode: .addSubview) { parentSize, newImageSize in
print("parentSize = \(parentSize)")
print("newImageSize = \(newImageSize)")
let sideLength:CGFloat = 90
let indent:CGFloat = 15
return CGRect(x: parentSize.width-sideLength-indent, y: indent, width: sideLength, height: sideLength)
}
}
}

- 24,482
- 9
- 132
- 127
-
How could I Customize the image that I add? As in make its corners round... Or ad labels? – Sep 20 '19 at 01:19
-
@NCT127 this is difficult question. This is depends what results do you have. I think it will be better If you can create separate question with code sharing. I can help you. – Vasily Bodnarchuk Sep 23 '19 at 19:23
-
Since iOS 10 you can use the UIGraphicImageRenderer, which has a better syntax and has some great features!
Swift 4
let renderer = UIGraphicsImageRenderer(size: view.bounds.size)
let image = renderer.image { context in
// draw your image into your view
context.cgContext.draw(UIImage(named: "myImage")!.cgImage!, in: view.frame)
// draw even more...
context.cgContext.setFillColor(UIColor.red.cgColor)
context.cgContext.setStrokeColor(UIColor.black.cgColor)
context.cgContext.setLineWidth(10)
context.cgContext.addRect(view.frame)
context.cgContext.drawPath(using: .fillStroke)
}

- 1,667
- 16
- 22
-
Bravo for this answer, that doesn't require a pre-existing image. This would be useful, for example in a function that converts CTFont()) character into an image (with `ctx.showGlyphs()`) – clearlight Apr 25 '22 at 16:35
Updated Answer: Once you get the From and To coordinates, here is how to draw a line in a UIImage with those coordinates. From and To coordinates are in image pixels.
func drawLineOnImage(size: CGSize, image: UIImage, from: CGPoint, to: CGPoint) -> UIImage {
// begin a graphics context of sufficient size
UIGraphicsBeginImageContext(size)
// draw original image into the context
image.drawAtPoint(CGPointZero)
// get the context for CoreGraphics
let context = UIGraphicsGetCurrentContext()
// set stroking width and color of the context
CGContextSetLineWidth(context, 1.0)
CGContextSetStrokeColorWithColor(context, UIColor.blueColor().CGColor)
// set stroking from & to coordinates of the context
CGContextMoveToPoint(context, from.x, from.y)
CGContextAddLineToPoint(context, to.x, to.y)
// apply the stroke to the context
CGContextStrokePath(context)
// get the image from the graphics context
let resultImage = UIGraphicsGetImageFromCurrentImageContext()
// end the graphics context
UIGraphicsEndImageContext()
return resultImage }

- 433
- 5
- 9
-
2Elaborating on how this code answers the question will help future visitors. – JAL Jan 25 '17 at 18:24
-
sir, could you please help me on this one ? https://stackoverflow.com/questions/49919853/how-to-draw-doodle-line-on-uiimage-in-swift – sarah Apr 19 '18 at 11:24