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} }
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 ".
- 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.
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{returnNSImage()}
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.
À 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 :
classLigne: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?NSColorelse { returnnil } 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") } } classTexte: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?NSColorelse { returnnil } 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") } } classTrucData: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) ... }
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.
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 iflet 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{returnNSImage()}
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".
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.
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)
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) } }
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 }
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 ".
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 forcharin 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 :
Réponses
Nooonnnnn pitié !
OK, juste pour toi mon petit :
* dans le coma , ne voit rien, n'entend rien *
Draken, tu es sur de çà :
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.
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.
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.
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)
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.
À 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 :
Puis tu peux l'enregistrer en fichier comme :
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.
Si tu enregistres une image, qu'est-ce tu fasses lors d'une mise jour des données ?
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 :
Bon çà marche mais j'avoue qu'il y a certaines instruction que je ne comprends pas dans le "snapshot".
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 :
et dans la vue :
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.
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.
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-ID121
Premier paragrapfh et en-dessous la rubrique Control Transfer Statements - comme attendu
Oh Yes, merci
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.
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.
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.
On peut faire la même chose sans l'instruction continue, pour alléger le code.
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).
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 :
On peut utiliser un opérateur d'énumération pour récupérer l'objet ET son indice :
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.
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 :
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 ?
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 ".
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 :
Je viens de taper ce code et de le tester. Exemple en image :
mais c'est la rotation à 90° qui m'intéresse