web-dev-qa-db-fra.com

Comment dessiner sur une image dans Swift?

Je dois pouvoir dessiner par programme sur une image et enregistrer cette image pour une utilisation ultérieure. Dites, tracez une ligne sur des coordonnées x et y spécifiques sur l'image, enregistrez l'image et affichez-la sur un simple contrôleur de vue. Comment pourrais-je faire cela dans Swift? (De préférence Swift 2, je suis toujours en développement et je n'ai pas mis à jour mon mac vers Sierra))

Mise à jour: Peut-être quelque chose à voir avec la conversion d'un UIImage en CGLayer, en s'appuyant dessus, puis en le reconvertissant en UIImage.

17
caleb

C'est simple:

  1. Créez un contexte graphique d'image. (Avant iOS 10, vous le feriez en appelant UIGraphicsBeginImageContextWithOptions. Dans iOS 10, il existe une autre façon, UIGraphicsImageRenderer, mais vous n'êtes pas obligé de l'utiliser si vous ne le souhaitez pas.)

  2. Dessinez (c'est-à-dire copiez) l'image dans le contexte. (UIImage a en fait draw... méthodes à cet effet.)

  3. Tracez votre ligne dans le contexte. (Il existe des fonctions CGContext pour cela.)

  4. Extraire l'image résultante du contexte. (Par exemple, si vous avez utilisé UIGraphicsBeginImageContextWithOptions, vous utiliseriez UIGraphicsGetImageFromCurrentImageContext.) Fermez ensuite le contexte.

10
matt

Tout ce que vous devez faire est de créer et d'obtenir un objet Contexte d'image et d'accéder à toutes ses méthodes de dessin puissantes. Vous pouvez en savoir plus sur les fonctionnalités de l'objet CGContext ici .

Cette fonction trace une ligne et un cercle sur un UIImage et retourne l'image modifiée:

Swift 4

func DrawOnImage(startingImage: UIImage) -> UIImage {

     // Create a context of the starting image size and set it as the current one
     UIGraphicsBeginImageContext(startingImage.size)

     // Draw the starting image in the current context as background       
     startingImage.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
}
19
Jorge Ramírez

Détails

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)
        }
    }
}

Exemples d'images

Image parent:

enter image description here

Image enfant:

enter image description here


Exemple d'utilisation 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)
    }
}

Résultat 1

enter image description here


Exemple d'utilisation 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)
    }
}

Résultat 2

enter image description here


Exemple d'utilisation 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)
    }
}

Résultat 3

enter image description here

Exemple de code complet

N'oubliez pas d'ajouter le code de solution ici

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)
        }
    }
}
8
Vasily Bodnarchuk

Depuis iOS 10, vous pouvez utiliser IGraphicImageRenderer , qui a une meilleure syntaxe et des fonctionnalités intéressantes!

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)
}
5
appsunited

Réponse mise à jour: Une fois que vous avez obtenu les coordonnées De et À, voici comment tracer une ligne dans un UIImage avec ces coordonnées. Les coordonnées From et To sont en pixels de l'image.

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 }
5
Nadeesha Lakmal