How do I draw on an image in Swift?
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}
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.
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 UIKitclass 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) } }}