How do I draw on an image in Swift?

前端 未结 5 1502
深忆病人
深忆病人 2020-12-09 04:18

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

相关标签:
5条回答
  • 2020-12-09 04:25

    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)
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-09 04:27

    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 }
    
    0 讨论(0)
  • 2020-12-09 04:33

    It's simple:

    1. 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.)

    2. Draw (i.e. copy) the image into the context. (UIImage actually has draw... methods for this very purpose.)

    3. Draw your line into the context. (There are CGContext functions for this.)

    4. Extract the resulting image from the context. (For example, if you used UIGraphicsBeginImageContextWithOptions, you would use UIGraphicsGetImageFromCurrentImageContext.) Then close the context.

    0 讨论(0)
  • 2020-12-09 04:40

    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
    }
    
    0 讨论(0)
  • 2020-12-09 04:47

    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)
    }
    
    0 讨论(0)
提交回复
热议问题