Comment dessiner des lignes et écrire du texte sur une image

2

Réponses

  • Nooonnnnn pitié !

  • Joanna CarterJoanna Carter Membre, Modérateur

    OK, juste pour toi mon petit :



    protocol AFMCentreOfGravityEnvelopeDrawingVisitor
    {
    var centreOfGravityEnvelope: AFMCentreOfGravityEnvelope

    var weightUnit: AFMWeightUnit


    var minimumAircraftWeight: Int { get }

    var maximumAircraftWeight: Int { get }

    var minimumCoG: CGFloat { get }

    var maximumCoG: CGFloat { get }


    var leftLegendMargin: CGFloat { get }

    var bottomLegendMargin: CGFloat { get }

    var outerMargin: CGFloat { get }


    var xAxisZero: CGFloat { get }

    var xAxisMaximum: CGFloat { get }

    var yAxisZero: CGFloat { get }

    var yAxisMaximum: CGFloat { get }


    var xAxisLabelText: String { get }

    var yAxisLabelText: String { get }

    var xAxisMajorTickY: CGFloat { get }

    var xAxisMinorTickY: CGFloat { get }

    var yAxisMajorTickX: CGFloat { get }

    var yAxisMinorTickX: CGFloat { get }


    var graphOrigin: CGPoint { get }

    var graphSize: CGSize { get }

    var graphBounds: CGRect { get }


    var xAxisScaleFactor: CGFloat { get }

    var yAxisScaleFactor: CGFloat { get }


    func drawHorizontalGridLine(for weight: CGFloat)

    func drawVerticalGridLine(for coG: CGFloat)

    func drawGridLines()


    func drawXMajorTickMark(for coG: CGFloat)

    func drawXMajorTickMarkLabel(for coG: CGFloat)

    func drawXMinorTickMark(for coG: CGFloat)

    func drawXTickMarks()

    func drawXAxisLine()


    func drawYMajorTickMark(for weight: CGFloat)

    func drawYMajorTickMarkLabel(for weight: CGFloat)

    func drawYMinorTickMark(for weight: CGFloat)

    func drawYTickMarks()

    func drawYAxisLine()


    func drawXAxisLabel()

    func drawYAxisLabel()


    func drawAxes()


    func drawTitle()


    var text12Attributes: NSDictionary { get }

    var text14Attributes: NSDictionary { get }

    var text14BoldAttributes: NSDictionary { get }
    }
  • * dans le coma , ne voit rien, n'entend rien *

  • Draken, tu es sur de çà  :



    @IBOutlet weak var vueCalque: ViewAvecCalque!

    je ne comprend pas


  • DrakenDraken Membre
    juillet 2017 modifié #36


    Draken, tu es sur de çà  :



    @IBOutlet weak var vueCalque: ViewAvecCalque!

    je ne comprend pas




    Oui, j'en suis certain. D'autant plus que c'est Storyboard qui a généré cette ligne quand j'ai demandé un IBOulet sur une vue personnalisée.


     


    On peux aussi créer la ViewAvecCalque avec du code, sans passer par Storyboard,  à  l'ancienne. 


  • DrakenDraken Membre
    juillet 2017 modifié #37

    Ce n'est pas compliqué d'utiliser une vue personnalisée dans Storyboard.


     


    La technique habituelle, que tu connais, c'est de glisser-déposer des composants graphiques de base (NSImageView, NSLabel, NSView, etc.) sur l'écran, et de tirer des IBOutlet vers le code pour les utiliser. La démarche est presque similaire. 


     


    Etape 1 : On glisse-déposer sur l'écran un composant graphique " Custom View ".


  • Etape 2 :


     


    - On sélectionne la "Custom View " et on affiche l'inspecteur d'identité.


    - L'option " Custom Class " permet de définir la classe à  associer avec la " Custom View ". Il suffit de cliquer sur l'option " Class " pour voir une (longue) liste de classes possibles. ViewAvecCalque étant créée à  partir de NSView, Xcode l'ajoute automatiquement dans la liste. 


    - Une fois " ViewAvecCalque " sélectionnée, Xcode sait que la " Custom View " appartient à  cette classe. En faisant un IBOutlet sur cette vue, il génère automatiquement les bons paramètres.



    @IBOutlet weak var vueCalque: ViewAvecCalque!

  • PatyomPatyom Membre
    juillet 2017 modifié #39

    Merci, hier soir en cherchant un peu sur Web, j'avais trouvé tout ce que tu viens de me présenter.


    Cà  s'affiche parfaitement, mes lignes se positionnent exactement aux bons endroits, le rêve.


     


    Par contre je voudrais enregistrer cette customView dans un fichier image pour ensuite l'afficher à  volonté dans d'autre applis.


     


    Je cherche encore mais si tu as une idée je suis preneur. 


  • DrakenDraken Membre
    juillet 2017 modifié #40

    Je t'avais donné un lien sur du code permettant de stocker l'image d'une NSView dans une NSImage, non ?


     


    https://pastebin.com/d4puXBi8


     


    Ce n'était pas en Swift 3, mais cela m'a pris quelques secondes pour l'adapter (le temps de valider les deux corrections suggérées par Xcode)



    extension NSView {
    var snapshot: NSImage {
    guard let bitmapRep = bitmapImageRepForCachingDisplay(in: bounds) else { return NSImage() }
    bitmapRep.size = bounds.size
    cacheDisplay(in: bounds, to: bitmapRep)
    let image = NSImage(size: bounds.size)
    image.addRepresentation(bitmapRep)
    return image
    }
    }


    Exemple d'utilisation :


     


    J'ai utilisé Storyboard pour placer sur l'écran, une vue avec calque, une imageView et un détecteur de clic. Quand l'utilisateur clique sur le calque, le programme fait une copie d'écran de son contenu, pour l'afficher dans l'imageView.



    @IBAction func actionClic(_ sender: NSClickGestureRecognizer) {
    let copieEcran = vueCalque.snapshot
    imageView.image = copieEcran
    }


  • Joanna CarterJoanna Carter Membre, Modérateur

    À la place d'enregistrer une image, je te conseillerais d'enregistrer les données.


     


    D'abord il faut que tous les types se conforme à  NSCoding :



    class Ligne : NSCoding
    {
    var point1 : NSPoint

    var point2 : NSPoint

    var width : CGFloat

    var couleur : NSColor

    required init?(coder aDecoder: NSCoder)
    {
    guard let couleur = aDecoder.decodeObject(forKey: "couleur") as? NSColor else
    {
    return nil
    }

    self.point1 = aDecoder.decodePoint(forKey: "point1")

    self.point2 = aDecoder.decodePoint(forKey: "point2")

    width = CGFloat(aDecoder.decodeDouble(forKey: "width"))

    self.couleur = couleur
    }

    func encode(with aCoder: NSCoder)
    {
    aCoder.encode(point1)

    aCoder.encode(point2)

    aCoder.encode(Double(width), forKey: "width")

    aCoder.encode(couleur, forKey: "couleur")
    }
    }

    class Texte : NSCoding
    {
    var point : NSPoint

    var text : String

    var police : NSFont

    var couleur : NSColor

    required init?(coder aDecoder: NSCoder)
    {
    guard let text = aDecoder.decodeObject(forKey: "text") as? String,
    let police = aDecoder.decodeObject(forKey: "police") as? NSFont,
    let couleur = aDecoder.decodeObject(forKey: "couleur") as? NSColor else
    {
    return nil
    }

    self.point = aDecoder.decodePoint(forKey: "point")

    self.text = text

    self.police = police

    self.couleur = couleur
    }

    func encode(with aCoder: NSCoder)
    {
    aCoder.encode(point, forKey: "point")

    aCoder.encode(text, forKey: "text")

    aCoder.encode(police, forKey: "width")

    aCoder.encode(couleur, forKey: "couleur")
    }
    }


    class TrucData : NSCoding
    {
    var lignes1 : [Ligne]?

    var lignes2 : [Ligne]?

    var lignes3 : [Ligne]?

    var textes1 : [Texte]?

    var textes2 : [Texte]?

    var textes3 : [Texte]?

    var image1 : NSImage?

    var image2 : NSImage?

    var image3 : NSImage?

    init() { }

    required init?(coder aDecoder: NSCoder)
    {
    lignes1 = aDecoder.decodeObject(forKey: "lignes1") as? [Ligne]

    lignes2 = aDecoder.decodeObject(forKey: "lignes2") as? [Ligne]

    lignes3 = aDecoder.decodeObject(forKey: "lignes3") as? [Ligne]

    textes1 = aDecoder.decodeObject(forKey: "textes1") as? [Texte]

    textes2 = aDecoder.decodeObject(forKey: "textes2") as? [Texte]

    textes3 = aDecoder.decodeObject(forKey: "textes3") as? [Texte]

    image1 = aDecoder.decodeObject(forKey: "image1") as? NSImage

    image2 = aDecoder.decodeObject(forKey: "image2") as? NSImage

    image3 = aDecoder.decodeObject(forKey: "image3") as? NSImage
    }

    func encode(with aCoder: NSCoder)
    {
    aCoder.encode(lignes1, forKey: "lignes1")

    aCoder.encode(lignes2, forKey: "lignes2")

    aCoder.encode(lignes3, forKey: "lignes3")

    aCoder.encode(textes1, forKey: "textes1")

    aCoder.encode(textes2, forKey: "textes2")

    aCoder.encode(textes3, forKey: "textes3")

    aCoder.encode(image1, forKey: "image1")

    aCoder.encode(image2, forKey: "image2")

    aCoder.encode(image3, forKey: "image3")
    }
    }

    Puis tu peux l'enregistrer  en fichier comme :



    {
    let trucData = TrucData()

    // initialiser data

    ...

    // pour le stocker comme fichier

    guard let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else
    {
    return
    }

    let fileURL = documentsURL.appendingPathComponent("TrucData.dat")

    NSArchiver.archiveRootObject(trucData, toFile: fileURL.path)

    ...
    }
  • Ok, la transformation en image marche, cool.


    Je suis en train de jongler avec le temps, je suis dans le Lot et cà  claque pas mal depuis 2 jours avec une pluie d'enfer. C'est vachement rare aussi fort et dans la durée dans le coin. Alors, je branche la box, je la débranche et ainsi de suite, c'est devenu mon quotidien.


     


    Il ne me reste plus que l'enregistrement mais çà  je sais faire mais pour tout à  l'heure.


  • Joanna CarterJoanna Carter Membre, Modérateur

    Si tu enregistres une image, qu'est-ce tu fasses lors d'une mise jour des données ?


  • PatyomPatyom Membre
    juillet 2017 modifié #44

    Je te rassure, une les images crées je ne reviens plus dessus et si vraiment je dois rajouter un élément, je dois le faire sur toutes les images de mon dossier et là  je recommence tout le calcul, c'est hyper rapide. Donc j'efface tout et je recalcule.


     


    C'est bon je l'ai enregistrée, mon code :



    // Enregistrement totale de l'image
    if let imageDef = imageVue.image?.imagePNGRepresentation() {
    let chemin = "/Users/PATRICK/Documents/APP-SAISIE/"
    imageDef.write(toFile: "\(chemin)ImageGraph3.png", atomically: false)
    }


    extension NSView {
    var snapshot: NSImage {
    guard let bitmapRep = bitmapImageRepForCachingDisplay(in: bounds) else { return NSImage() }
    bitmapRep.size = bounds.size
    cacheDisplay(in: bounds, to: bitmapRep)
    let image = NSImage(size: bounds.size)
    image.addRepresentation(bitmapRep)
    return image
    }


    }

    Bon çà  marche mais j'avoue qu'il y a certaines instruction que je ne comprends pas dans le "snapshot".


  • Joanna CarterJoanna Carter Membre, Modérateur
    juillet 2017 modifié #45

    Donc, après que tu as enregistré les images, tu fait quoi pour les afficher à  la place du "ViewAvecCalque" ?


     


    Forcément, c'est plus facile d'enregistrer les données et de "rafraà®chir" la vue lors d'un mise à  jour dans le didSet de la var "representedObject" ?


     


    Les données occuperont beaucoup moins d'espace sur le disque et tu pourrait changer "l'image" dans une seule ligne de code :



    {
    viewCalque.representedObject = chargerFichier(de url: ...)

    ...
    }

    et dans la vue :



    override var representedObject: Any?
    {
    didSet
    {
    view.setNeedsDisplay(view.bounds)
    }
    }




  •  


    Bon çà  marche mais j'avoue qu'il y a certaines instruction que je ne comprends pas dans le "snapshot".




    Moi non plus, je ne comprend pas tout. J'ai juste recopié un code venant d'internet. Tu peux regarder dans la doc Apple pour comprendre ces instructions. Pour ma part, cela ne m'intéresse pas, c'est trop spécifique à  macOS, alors que ma tasse de thé c'est iOS. Il serait temps qu'Apple unifie certaines API. Cocoa est vraiment en retard par rapport à  UIKit.


     


     


     


     

  • PatyomPatyom Membre
    juillet 2017 modifié #47

    Quelle flopée d'instruction dans la doc en ligne d'Apple, je m'y perd.


     


    Tiens une autre, dans une boucle "for in" je voudrais sauter ne pas traiter une ligne, comment fait-on pour sauter une ligne dans ce genre de boucle.


  • Joanna CarterJoanna Carter Membre, Modérateur
    juillet 2017 modifié #48
  • Oh Yes, merci


  • DrakenDraken Membre
    juillet 2017 modifié #50

    L'exemple d'Apple me parait un brin tiré par les cheveux. Bon d'accord c'est juste un exemple pédagogique pour illustrer l'instruction continue, mais quand même.



    let puzzleInput = "great minds think alike"
    var puzzleOutput = ""
    let charactersToRemove: [Character] = ["a", "e", "i", "o", "u", " "]
    for character in puzzleInput {
    if charactersToRemove.contains(character) {
    continue
    } else {
    puzzleOutput.append(character)
    }
    }
    print(puzzleOutput)

    En plus il ne FONCTIONNE PAS ! Le type qui l'a tapé ne l'a pas testé, ou alors cela correspond à  une vieille syntaxe de Swift 1, abandonnée par la suite .. La doc Apple manque souvent de mise à  jour, surtout dans les exemples.  



    // Mauvaise syntaxe (le compilateur hurle à  la compilation)
    for character in puzzleInput {
    }


    On ne peut pas énumérer des characters à  partir d'un String comme ça. Il faut ajouter l'opérateur d'énumération .characters pour que Xcode comprenne qu'il doit interpréter le String comme un tableau de characters.



    // Bonne syntaxe (pour Swift 3.1)
    for character in puzzleInput.characters {

    On peut faire la même chose sans l'instruction continue, pour alléger le code.



    let puzzleInput = "great minds think alike"
    var puzzleOutput = ""
    let charactersToRemove: [Character] = ["a", "e", "i", "o", "u", " "]

    for character in puzzleInput.characters {
    if !charactersToRemove.contains(character) {
    puzzleOutput.append(character)
    }
    }
    print(puzzleOutput)
     

  • Joanna CarterJoanna Carter Membre, Modérateur
    juillet 2017 modifié #51

    Désolée Draken, c'est pas les docs qui ne sont pas à  jour, c'est toi  ::)


     


    La syntaxe est bien à  jour pour Swift 4  8--)


     


    Il faut regarder le haut de la page


  • DrakenDraken Membre
    juillet 2017 modifié #52


    Désolée Draken, c'est pas les docs qui ne sont pas à  jour, c'est toi  ::)


     


    La syntaxe est bien à  jour pour Swift 4  8--)


     


    Il faut regarder le haut de la page




     


    Oué, oué * ronchonne *


     


    N'empêche que Patyom bosse avec Swift 3, lui ! Comme tous les dévs en production .. Xcode 4 c'est dans 2 ou 3 mois, pas avant ..


     


    EDIT : Effectivement, ça marche. je viens de tester avec Swift 4 (bêta Xcode 9v3). 



  • if textHeure.contains(":") { } else {
    continue // suite de la boucle
    }

    Code pour vérifier que l'heure que j'ai extraite est au format "HH:MM", si true je fais le traitement sinon via "continue" je prends la ligne suivante.


    Cà  vous parait correct ?


     


    il y a beaucoup de changements dans Swift 4 par rapport à  Swift 3 ?


  • Il existe une autre manière de " sauter " une itération de la boucle for in : le test sur la valeur numérique de l'indice.


     


    La boucle for in ne gère pas l'indice de boucle, en fournissant directement les objets lus. Pour moi c'est l'une des principales innovations du langage, qui n'en manque pas pourtant ! Toujours plus simple, toujours plus lisible, en avant vers l'infini ...


     


    Voici un petit code affichant une liste de noms :



    let liste = ["ZERO", "UN", "DEUX", "TROIS", "QUATRE", "CINQ "]

    for nom in liste {
    print (nom)
    }



    ZERO
    UN
    DEUX
    TROIS
    QUATRE
    CINQ


    On peut utiliser un opérateur d'énumération pour récupérer l'objet ET son indice :



    for (index,nom) in liste.enumerated() {
    if index != 3 {
    print (nom)
    }
    }



     


    ZERO


    UN


    DEUX


    QUATRE


    CINQ


     




  • if textHeure.contains(":") { } else {
    continue // suite de la boucle
    }

    Code pour vérifier que l'heure que j'ai extraite est au format "HH:MM", si true je fais le traitement sinon via "continue" je prends la ligne suivante.


    Cà  vous parait correct ?


     




     


    C'est correct, mais c'est un peu bizarre comme manière de l'écrire. Moi j'aurais utilisé l'opérateur de négation (!) pour alléger l'écriture.



    if !textHeure.contains(":") {
    continue // suite de la boucle
    }



  • il y a beaucoup de changements dans Swift 4 par rapport à  Swift 3 ?


     




    Beaucoup moins qu'entre Swift 2 et Swift 3 ! Heureusement d'ailleurs. 

  • Ca y est je me suis lancé sur l'affichage des textes et bien entendu j'ai des questions :




    func creerTextes1() -> [TypeTexte] {

    let t1 = TypeTexte(point: NSMakePoint(15, 40),
    text: "VuePersonnalisée 1",
    police: NSFont.boldSystemFont(ofSize: 17.0),
    couleur: NSColor.blue)

    var tableauTexte = [TypeTexte]()
    tableauTexte.append(t1)
    return tableauTexte

    J'ai donc récupéré le code correspondant et là  je dois écrire le long de ces lignes VERTICALES, pas facile l'instruction.


     


    "il a toujours quelques chose de compliquer à  faire" vous devez vous dire non ?


  • DrakenDraken Membre
    juillet 2017 modifié #58

    C'est quelque part là -dedans. Bonne chance :


     


    https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/CoreText_Programming/Introduction/Introduction.html#//apple_ref/doc/uid/TP40005533


     


    Sinon, pour éviter d'aller farfouiller dans l'immensité de la doc Apple et la complexité d'utilisation de CoreText, il reste la bonne vieille solution : afficher les caractères un par un, en calculant leurs positions d'affichage à  la " main ".

  • PatyomPatyom Membre
    juillet 2017 modifié #59

    j'ai trouvé çà  :


     


    https://stackoverflow.com/questions/38409634/how-to-rotate-text-in-nsview-mac-os-cocoa-swift


    (le code dans la partie basse)


    Qu'en pensez-vous, je n'arrive pas à  implanter le code dans le prog.


  • Cela ne résoudra pas ton problème, ce code effectue une rotation à  90° de l'ensemble du texte. Je doute que tes utilisateurs aient envie de tourner la tête à  angle droit pour lire les infos de ton application.


     


    Essaye plutôt ça :



    func drawTexteVertical(texte:String,
    police:NSFont,
    couleur:NSColor,
    position:NSPoint) {
    var posCourante = position
    for char in texte.characters {
    let attrStr = NSAttributedString(string: String(char),
    attributes: [
    NSFontAttributeName : police,
    NSForegroundColorAttributeName : couleur])
    attrStr.draw(at: posCourante)
    posCourante.y -= police.capHeight + 1
    }
    }

    Je viens de taper ce code et de le tester. Exemple en image :


     


     


     


  • mais c'est la rotation à  90° qui m'intéresse


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