drawLines: l'élément se dessine dans une position très en-dessous du curseur

Bonjour,

J'ai créé une vue où on peut dessiner, mais quand je dessine, la ligne est créée très en dessous du curseur ou du doigt.
La ligne rouge est le mouvement de mon doigt ou du curseur et la ligne noire est ce qui est dessiné.

class DrawViewController: UIViewController {

        @IBOutlet weak var imageView: UIImageView!
        var lastPoint = CGPoint.zero
        var swiped = false

        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?){
            swiped = false
            if let touch = touches.first {
                lastPoint = touch.location(in: self.view)
            }
        }

        func drawLines(fromPoint:CGPoint,toPoint:CGPoint) {

            UIGraphicsBeginImageContext(self.view.frame.size)
            imageView.image?.draw(in: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
            // imageView.image?.draw(in: view.bounds)
            let context = UIGraphicsGetCurrentContext()

            context?.move(to: CGPoint(x: fromPoint.x, y: fromPoint.y))
            context?.addLine(to: CGPoint(x: toPoint.x, y: toPoint.y))
            // context.move(to: fromPoint)
            // context.addLine(to: toPoint)

            context?.setBlendMode(CGBlendMode.normal)
            context?.setLineCap(CGLineCap.round)
            context?.setLineWidth(5.0)
            context?.setStrokeColor(UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0).cgColor)

            context?.strokePath()

            imageView.image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
        }

        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?){
            swiped = true
            if let touch = touches.first {
                let currentPoint = touch.location(in: self.view)
                drawLines(fromPoint: lastPoint, toPoint: currentPoint)
                lastPoint = currentPoint
            }
        }

        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?){ if !swiped { drawLines(fromPoint: lastPoint, toPoint: lastPoint) } }

        override func viewDidLoad() {super.viewDidLoad()}
        override func didReceiveMemoryWarning() {super.didReceiveMemoryWarning()}
Mots clés:

Réponses

  • DrakenDraken Membre
    septembre 2018 modifié #2

    A première vue, l'écart entre les deux lignes a la même taille que la barre de statut en haut.

    Je pense que l'erreur vient de là :

    let currentPoint = touch.location(in: self.view)

    Tu consultes la position de touch par rapport à la vue principale (self.view), alors que tu dessines dans une ImageView placée plus bas sur l'écran, ce qui engendre un décalage.

    Il faut calculer la position du touch par rapport à l'imageView ..

  • bonjour,

    J'ai modifié touch.location(in: self.imageView) mais ça ma donné ce ci:

    j'ai donc suivie vos deux dernières phrases et remplacer view par imageView dans les fonctions et tout fonctionne parfaitement.

    merci de votre aide.

  • Est-ce que tu as corrigé le code de conversion dans le touchBegan et le touchMoved ? Si tu ne le fait que dans le touchMoved la position de départ est erronée, ce qui peut expliquer l'allure du dessin.

  • oui je les ai modifiées après avoir obtenu le résultat montré.

  • problèmesproblèmes Membre
    septembre 2018 modifié #6

    Bonjour, j'ai remarqué aussi que quand je fais plein de trait, le dessein remonte légèrement et devient de plus en plus flou à chaque trait.

    J'ai remplacer UIGraphicsBeginImageContext(self.view.frame.size) par ce code là UIGraphicsBeginImageContextWithOptions(imageView.frame.size, false, 0) pour régler le problème.

  • Si tu veux obtenir la position d'un événement souris sur une vue, quelque soit sa position et sa taille sur l'écran, il faut utiliser la fonction hitTest(). Cela permet de dire à une vue "il vient de se produire un événement utilisateur. Est-ce qu'une de tes sous-vues est concernée ?"

            override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
                if let touch = touches.first {
                    let positionGlobale = touch.location(in: self.view)
                    if self.view.hitTest(positionGlobale, with: event) == imageView {
                        let position = touch.location(in: imageView)
                        print ("Position : ", position)
                    }
                }
            }
    

    Petite subtilité : hitTest() ne prend en compte que les sous-vues sensibles aux événements tactiles. Il faut cocher la case "User Interaction Enabled" d'imageView, dans le Storyboard pour que cela fonctionne.

  • DrakenDraken Membre
    septembre 2018 modifié #8

    Bonjour, j'ai remarqué aussi que quand je fais plein de trait, le dessein remonte légèrement et devient de plus en plus flou à chaque trait.

    Je pense que le problème vient de la manière dont tu recopie le contenu de l'image tampon sur l'écran. Tu crée un contexte de dessin bitmap de la taille de l'écran.

    UIGraphicsBeginImageContext(self.view.frame.size)

    Que tu copie ensuite dans l'imageView.

    imageView.image = UIGraphicsGetImageFromCurrentImageContext()

    Mais les deux n'ont pas la même taille, à cause de la barre de statut. Cela produit une déformation de l'image à chaque opération graphique.

    J'ai repris ton code à ma sauce. La seule grosse différence est que cela fonctionne avec une imageView de n'importe quelle taille. Pour le test j'ai utilisé des contraintes de 40 points sur les dimensions de l'imageView pour qu'elle soit plus petite que l'écran. Et j'ai utilisé une couleur de fond bleue pour visualiser graphiquement la zone de dessin.

    La taille du contexte graphique est calculée d'après l'imageView.

    Il n'y a aucune bavure graphique, même avec beaucoup de traits :

    La vue globale :

    Le code :

        import UIKit
    
        class ViewController: UIViewController {
    
            @IBOutlet weak var imageView: UIImageView!
            var dernierePosition = CGPoint.zero
    
            override func viewDidLoad() {
                super.viewDidLoad()
            }
    
            override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
                if let touch = touches.first {
                    let positionGlobale = touch.location(in: self.view)
                    if self.view.hitTest(positionGlobale, with: event) == imageView {
                        dernierePosition = touch.location(in: imageView)
                    }
                }
            }
    
            override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?){
                if let touch = touches.first {
                    let positionGlobale = touch.location(in: self.view)
                    if self.view.hitTest(positionGlobale, with: event) == imageView {
                        let positionCourante = touch.location(in: imageView)
                        drawLines(fromPoint: dernierePosition, toPoint: positionCourante)
                        dernierePosition = positionCourante
                    }
                }
            }
    
            func drawLines(fromPoint:CGPoint,toPoint:CGPoint) {
    
                UIGraphicsBeginImageContext(self.imageView.frame.size)
                imageView.image?.draw(in: CGRect(x: 0, y: 0, width: self.imageView.frame.width, height: self.imageView.frame.height))
                let context = UIGraphicsGetCurrentContext()
    
                context?.move(to: CGPoint(x: fromPoint.x, y: fromPoint.y))
                context?.addLine(to: CGPoint(x: toPoint.x, y: toPoint.y))
    
                context?.setBlendMode(CGBlendMode.normal)
                context?.setLineCap(CGLineCap.round)
                context?.setLineWidth(5.0)
                context?.setStrokeColor(UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0).cgColor)
    
                context?.strokePath()
    
                imageView.image = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
            }
    
        }
    
Connectez-vous ou Inscrivez-vous pour répondre.