[Résolu] Dessiner dans des UIView externes ou dans le ViewController

busterTheobusterTheo Membre
mars 2015 modifié dans API UIKit #1

Bonjour,


 


est-il préférable de dessiner (à  l'aide de drawRect, par exemple) dans des classes UIView perso que l'on instancie, ou plutôt, de dessiner sans ces instances, dans le ViewController de base ?


 


Merci d'avance


Réponses

  • AliGatorAliGator Membre, Modérateur
    Comment ça "dessiner dans un ViewController" ?

    Un ViewController par définition fait partie de la couche "Controller" du MVC.
    Alors que le dessiner c'est plutôt dans la couche "Vue".

    Alors le contrôleur peut prendre en charge le pilotage des paramétrages du dessin à  faire par la partie vue, comme fournir les valeurs d'angles d'un arc que la vue va avoir à  tracer ou la liste des points que la vue aura à  relier... mais au final les commandes de dessin avec CoreGraphics (CGContextXXX etc) c'est plutôt à  la vue de le faire, c'est justement son boulot...

    Ou alors j'ai pas compris la question...
  • busterTheobusterTheo Membre
    mars 2015 modifié #3

    Merci pour tes explication, mais je ne comprend plus trop.


     


    Donc ça, c'est mauvais ?


    Vidéo en ligne.


    Merci d'avance.



    // http://www.raywenderlich.com/90488/calayer-in-ios-with-swift-10-examples

    // Example #8: CAShapeLayer

    // ESSAI AVEC LE MASQUE - myLayerView - A PARTIR DUN LAYER - masqueLayer - DANS UNE VIEW - myView -

    import UIKit

    class ViewController: UIViewController, UIScrollViewDelegate {

    // Mark: - Les Propriétés

    @IBOutlet weak var fondImageView: UIImageView!

    // Mark: - Les variables

    var myView: UIView!
    var myScrollView:UIScrollView = UIScrollView()

    var myLayerView: UIView!
    var myLayerContourView: UIView!

    var myImageView = UIImageView()

    let masqueColor = UIColor(red: 0/255.0, green: 255/255.0, blue: 255/255.0, alpha: 1.0)
    let masquePath = UIBezierPath()
    let masqueLayer = CAShapeLayer()
    let masqueContourLayer = CAShapeLayer()

    // Mark: - View Life Cycle

    override func viewDidLoad() {
    super.viewDidLoad()

    construction()
    }

    func construction() {
    // LA VIEW - myView - ENGLOBANTE
    myView = UIView(frame: CGRectMake(262, 134, 500, 500))
    self.view.addSubview(myView)

    // LA SCROLLVIEW - myScrollView - QUI CONTIENT LA IMAGEVIEW - myImageView -
    myScrollView.delegate = self
    myScrollView.frame = CGRectMake(0, 0, 500, 500)
    myView.addSubview(myScrollView)

    // LA IMAGEVIEW - myImageView - DE LA PHOTO QUI SE DEPLACE DANS LE MASQUE
    let myImage = UIImage(named: "moi.jpg")!
    myImageView.image = myImage
    // myImageView.contentMode = UIViewContentMode.Center
    myImageView.frame = CGRectMake(0, 0, myImage.size.width, myImage.size.height)
    myScrollView.contentSize = myImage.size
    // myImageView.userInteractionEnabled = true

    // LA IMAGEVIEW - myImageView - DANS LA SCROLLVIEW - myScrollView -
    myScrollView.addSubview(myImageView)

    myScrollView.minimumZoomScale = 0.5
    myScrollView.maximumZoomScale = 8
    myScrollView.zoomScale = 1

    // LA VIEW - myLayerView - QUI CONTIENT LE LAYER DU MASQUE - masqueLayer - DANS LA VIEW ENGLOBANTE - myView -
    myLayerView = UIView(frame: CGRectMake(0, 0, 500, 500))
    myView.addSubview(myLayerView)

    // LE MASQUAGE
    myView.maskView = myLayerView

    // AJOUT DU LAYER - masqueLayer - DANS LA VIEW - myLayerView -
    pathMask()

    // LA VIEW - myLayerContourView - QUI CONTIENT LE LAYER DU CONTOUR DU MASQUE - masqueContourLayer - DANS LA VIEW ENGLOBANTE - myView -
    myLayerContourView = UIView(frame: CGRectMake(0, 0, 500, 500))
    myLayerContourView.userInteractionEnabled = false
    myView.addSubview(myLayerContourView)

    // AJOUT DU LAYER - masqueContourLayer - DANS LA VIEW - myLayerContourView -
    pathContourMask()
    }

    func pathMask() {
    masquePath.moveToPoint(CGPointMake(150, 200))
    masquePath.addCurveToPoint(CGPointMake(350, 200), controlPoint1: CGPointMake(150, 400), controlPoint2: CGPointMake(350, 400))
    masquePath.addCurveToPoint(CGPointMake(150, 200), controlPoint1: CGPointMake(350, 150), controlPoint2: CGPointMake(150, 150))
    masquePath.closePath()
    masqueLayer.path = masquePath.CGPath
    myLayerView.layer.addSublayer(masqueLayer)
    }

    func pathContourMask() {
    masquePath.moveToPoint(CGPointMake(150, 200))
    masquePath.addCurveToPoint(CGPointMake(350, 200), controlPoint1: CGPointMake(150, 400), controlPoint2: CGPointMake(350, 400))
    masquePath.addCurveToPoint(CGPointMake(150, 200), controlPoint1: CGPointMake(350, 150), controlPoint2: CGPointMake(150, 150))
    masquePath.closePath()
    masqueContourLayer.path = masquePath.CGPath
    masqueContourLayer.lineWidth = 8.0
    masqueContourLayer.strokeColor = UIColor.redColor().CGColor
    masqueContourLayer.fillColor = UIColor.clearColor().CGColor
    myLayerContourView.layer.addSublayer(masqueContourLayer)
    }

    func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
    return myImageView
    }
    }

  • Au départ, j'avais fait comme ça, et le résultat est exactement identique.


     


    (myForme - myContourFormes) UIView instanciées à  partir des fichiers (Formes.swift - ContourFormes.swift) qui ont les drawRect avec les méthode CGContext.


     


    J'avais cru comprendre d'après ce post que l'on pouvait dessiner dans le viewController.



    import UIKit

    class ViewController: UIViewController, UIScrollViewDelegate {

    // Mark: - Les Propriétés

    @IBOutlet weak var fondImageView: UIImageView!

    // Mark: - Les variables

    var myView: UIView!
    var myScrollView:UIScrollView = UIScrollView()

    var myImageView = UIImageView()

    var myForme: Formes!
    var myContourFormes: ContourFormes!

    override func viewDidLoad() {
    super.viewDidLoad()

    // LA VIEW ENGLOBANTE
    myView = UIView(frame: CGRectMake(262, 134, 500, 500))
    self.view.addSubview(myView)

    // LA SCROLLVIEW QUI CONTIENT LA IMAGEVIEW
    myScrollView.delegate = self
    myScrollView.frame = CGRectMake(0, 0, 500, 500)
    myView.addSubview(myScrollView)

    // LA IMAGEVIEW DE LA PHOTO
    let myImage = UIImage(named: "moi.jpg")!
    myImageView.image = myImage
    myImageView.contentMode = UIViewContentMode.Center
    myImageView.frame = CGRectMake(0, 0, myImage.size.width, myImage.size.height)
    myScrollView.contentSize = myImage.size
    myImageView.userInteractionEnabled = true
    myScrollView.addSubview(myImageView)

    myScrollView.minimumZoomScale = 1
    myScrollView.maximumZoomScale = 8
    myScrollView.zoomScale = 1

    // LA FORME DU MASQUE
    myForme = Formes(frame: CGRectMake(50,50,400,400))
    myView.addSubview(myForme)

    myView.maskView = myForme

    // LA FORME DU CONTOUR MASQUE
    myContourFormes = ContourFormes(frame: CGRectMake(50,50,400,400))
    myContourFormes.userInteractionEnabled = false
    myView.addSubview(myContourFormes)
    }

    func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
    return myImageView
    }
    }
  • AliGatorAliGator Membre, Modérateur
    mars 2015 modifié #5

    Donc ça, c'est mauvais ?

    Non, non "créer des CAShapeLayer dans le ViewController" comme ça, ce n'est pas mauvais. Mais c'est pas vraiment "dessiner dans le ViewController" ça.
    En fait c'est juste une question de vocabulaire dans ta question qui m'a gêné, laissant peut-être transparaà®tre une confusion entre Controller et Vue dans ta tête, donc j'ai voulu mettre les choses au carré.


    Un ViewController ne dessine pas, au sens où ce n'est pas à  lui d'appeler les méthodes CoreGraphics. Par contre il peut construite des UIBezierPath à  fournir au composant de la partie Vue pour lui dire quoi dessiner.

    C'est pour cela que j'ai précisé dans ma réponse que le contrôleur peut prendre en charge le pilotage des paramètres du dessin comme fournir la liste de points à  dessiner (ici fournir le BezierPath à  dessiner), mais ce n'est pas lui qui va dessiner le path à  l'écran. C'est le CAShapeLayer qui va faire effectivement le dessin. Le UIBezierPath est un objet modèle, qui décrit une courbe de bézier (la liste des points pour les courbes et leurs points de contrôles, etc), mais qui ne la dessine pas à  l'écran.
    C'est CAShapeLayer qui va prendre en entrée cet objet métier décrivant des paramètres (des CGPoint, des UIColor, des descriptions de points permettant de définir une courbe) et l'utiliser pour dessiner des pixels à  l'écran.



    Donc ton UIViewController manipule des UIBezierPath. Puis ensuite crée un CAShapeLayer et lui passe le UIBezierPath pour laisser le CALayer dessiner.
    Pour faire le parallèle, c'est le même principe que si ton UIViewController créait un NSArray de CGPoint puis le passait à  une sous-classe custom de UIView, qui utilisait ce tableau de CGPoint pour dessiner des lignes avec les fonctions CGContextAddLineToPoint() de CoreGraphics. Là  aussi le UIViewController ne manipule que des données métier (les CGPoint) mais c'est la UIView qui fait le dessin en lui-même. Comme pour CAShapeLayer.


    Donc dire "dessiner dans le UIViewController" comme tu l'as mis dans ta question initiale ça n'a pas de sens (ou plutôt c'est sans doute un abus de langage, mais il me semble important de préciser cette distinction, car c'est justement la séparation entre le contrôleur et le métier et si tu mélanges les deux en faisant des approximations de langages comme dire "Le UIViewController dessine", tu risques de vite mélanger les couches Controller et View dans ta tête ;))
  • AliGatorAliGator Membre, Modérateur
    Parce que bon au final si tu faisais l'autre solution, celle de ton 2ème code avec ta sous-classe Formes de UIView, finalement c'est le même principe, je ne sais pas comment tu avais prévu avec cette solution de fournir à  ta vue la liste des points / la courbe à  dessiner, mais tu aurais bien eu à  créer la définition de tes courbes et leurs points (avec un UIBezierPath par exemple ou un NSArray de CGPoint ou autre) quelque part, pour fournir cette liste de paramètre / de points à  ta vue Formes, histoire qu'elle sache quoi dessiner, non ?
  • Joanna CarterJoanna Carter Membre, Modérateur
    mars 2015 modifié #7

    De mon avis, tu devrais dériver une classe de UIView et créer là  dedans tous les elements visuels.


     


    Selon le design pattern MVC, la vue devrait comporter d'un protocole de délégué, et c'est par les méthodes de ce protocole que la vue peut faire les requêtes pour le nombre d'elements, leur positions, etc.


     


    C'est le contrôleur qui joue en fournissant les données en réponse aux requêtes de la vue mais, surtout, c'est la vue qui devrait prendre charge de créer et de dessiner les elements.


     


    Regardes-tu les docs sur UITableViewDataSource et UITableViewDelegate pour les idées de comment construire un tel protocol.


  • Personnellement, je suis d'avantage attiré par constituer des sous classes de UIView, ainsi le dessin est en dehors du fichier ViewController.


     


    J'avais cru comprendre que les layers étaient mieux pour l'animation.


     


    En fait, je n'ai pas trop besoin d'animation, je vais donc opté pour la version du dessin dans des sous classes de UIView, en plus je trouve ça plus propre, et plus clair surtout.


     


    Merci pour votre aide.


     


    J'ai bien noté, le MVC, les fonctions CoreGraphic dans la view, et justement, je cherche pour le tableau des points


     


    Je met résolu

Connectez-vous ou Inscrivez-vous pour répondre.