Les applications situées dans la barre de menus sont connues depuis longtemps des utilisateurs de macOS. Certaines de ces applications ont une partie «normale», d'autres se trouvent uniquement dans la barre de menus.
Dans ce guide, vous allez écrire une application qui affiche plusieurs citations de personnes célèbres dans une fenêtre pop-up. Au cours de la création de cette application, vous apprendrez:
- attribuer une icĂ´ne d'application dans la barre de menus
- faire l'application placée uniquement dans la barre de menu
- ajouter un menu personnalisé
- afficher une fenêtre contextuelle à la demande de l'utilisateur et la masquer si nécessaire, à l'aide de la surveillance des événements
Remarque: ce guide suppose que vous connaissez bien Swift et macOS.
Pour commencer
Lancez Xcode. Ensuite, dans le menu
Fichier / Nouveau / Projet ... , sélectionnez le modèle d'
application macOS / Application / Cocoa et cliquez sur
Suivant .
Sur l'écran suivant, entrez Devis comme
nom de produit , sélectionnez votre
nom d' organisation et votre
identifiant d'organisation . Assurez-vous ensuite que Swift est sélectionné comme langue d'application et que la case
Utiliser les storyboards est cochée. Décochez les
cases Créer une application basée sur un document ,
Utiliser les données de base ,
Inclure les tests unitaires et
Inclure les tests d'interface utilisateur .

Enfin, cliquez Ă nouveau sur
Suivant , spécifiez l'emplacement où enregistrer le projet et cliquez sur
Créer .
Une fois le nouveau projet créé, ouvrez
AppDelegate.swift et ajoutez la propriété suivante à la classe:
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
Ici, nous créons dans la barre de menu Élément d'état (icône d'application) une longueur fixe qui sera visible pour les utilisateurs.
Ensuite, nous devons affecter notre image à ce nouvel élément dans la barre de menu afin de pouvoir distinguer notre nouvelle application.
Dans le navigateur de projet, accédez à Assets.xcassets,
téléchargez une image et faites-la glisser dans le catalogue d'actifs.
Sélectionnez une image et ouvrez l'inspecteur d'attributs. Modifiez l'option
Rendre en tant qu'image de
modèle .

Si vous utilisez votre propre image, assurez-vous que l'image est en noir et blanc et configurez-la en tant
qu'image de modèle afin que l'icône soit superbe à la fois dans la barre de menu sombre et claire.
Revenez Ă
AppDelegate.swift et ajoutez le code suivant Ă
applicationDidFinishLaunching (_ :) if let button = statusItem.button { button.image = NSImage(named:NSImage.Name("StatusBarButtonImage")) button.action = #selector(printQuote(_:)) }
Ici, nous attribuons l'icĂ´ne d'application que nous venons d'ajouter Ă l'icĂ´ne d'application et attribuons une
action lorsque nous cliquons dessus.
Ajoutez la méthode suivante à la classe:
@objc func printQuote(_ sender: Any?) { let quoteText = "Never put off until tomorrow what you can do the day after tomorrow." let quoteAuthor = "Mark Twain" print("\(quoteText) — \(quoteAuthor)") }
Cette méthode imprime simplement le devis sur la console.
Faites attention Ă la
directive de méthode
objc . Cela vous permet d'utiliser cette méthode en réponse à un clic sur un bouton.
Générez et exécutez l'application, et vous verrez la nouvelle application dans la barre de menus. Hourra!
Chaque fois que vous cliquez sur l'icône dans la barre de menu, le célèbre dicton de Mark Twain s'affiche dans la console Xcode.
Nous masquons la fenĂŞtre principale et l'icĂ´ne dans le dock
Il y a quelques petites choses que nous devons faire avant de traiter directement avec la fonctionnalité:
- supprimer l'icĂ´ne du quai
- supprimer la fenĂŞtre principale de l'application inutile
Pour supprimer l'icĂ´ne du dock, ouvrez
Info.plist . Ajoutez une nouvelle clé
Application is agent (UIElement) et définissez sa valeur sur
YES .

Il est maintenant temps de traiter avec la fenĂŞtre principale de l'application.
- ouvrir Main.storyboard
- sélectionnez la scène du contrôleur de fenêtre et supprimez-la
- Voir la scène du contrôleur quitter, nous l'utiliserons bientôt

Générez et exécutez l'application. Maintenant, l'application n'a pas à la fois la fenêtre principale et l'icône inutile dans le dock. Super!
Ajouter un menu à l'élément d'état
Une réponse en un seul clic n'est clairement pas suffisante pour une application sérieuse. La façon la plus simple d'ajouter des fonctionnalités consiste à ajouter un menu. Ajoutez cette fonction à la fin d'
AppDelegate .
func constructMenu() { let menu = NSMenu() menu.addItem(NSMenuItem(title: "Print Quote", action: #selector(AppDelegate.printQuote(_:)), keyEquivalent: "P")) menu.addItem(NSMenuItem.separator()) menu.addItem(NSMenuItem(title: "Quit Quotes", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")) statusItem.menu = menu }
Et puis ajoutez cet appel Ă la fin de
applicationDidFinishLaunching (_ :) constructMenu()
Nous créons
NSMenu , y ajoutons 3 instances de
NSMenuItem et définissons ce menu comme menu d'icônes d'application.
Quelques points importants:
- Le titre de l'élément de menu est le texte qui apparaît dans le menu. Un bon endroit pour localiser l'application (si nécessaire).
- l'action , comme l' action d'un bouton ou d'un autre contrôle, est une méthode qui est appelée lorsque l'utilisateur clique sur un élément de menu
- keyEquivalent est un raccourci clavier que vous pouvez utiliser pour sélectionner un élément de menu. Les caractères minuscules utilisent Cmd comme modificateur et les caractères minuscules utilisent Cmd + Maj . Cela ne fonctionne que si l'application est tout en haut et est active. Dans notre cas, il est nécessaire que le menu ou une autre fenêtre soit visible, car notre application n'a pas d'icône dans le dock
- separatorItem est un élément de menu inactif sous la forme d'une ligne grise entre d'autres éléments. Utilisez-le pour regrouper
- printQuote est la méthode que vous avez déjà définie dans AppDelegate et terminate est la méthode définie par NSApplication .
Lancez l'application et vous verrez un menu en cliquant sur l'icĂ´ne de l'application.

Essayez de cliquer sur le menu - sélectionner
Imprimer le devis affiche le devis dans la console Xcode et
Quitter les devis met fin Ă l'application.
Ajouter une popup
Vous avez vu à quel point il est facile d'ajouter un menu à partir du code, mais afficher un devis dans la console Xcode n'est clairement pas ce que les utilisateurs attendent de l'application. Nous allons maintenant ajouter un simple contrôleur de vue pour afficher les devis de la bonne manière.
Allez dans le menu
Fichier / Nouveau / Fichier ... , sélectionnez le modèle de
classe macOS / Source / Cocoa et cliquez sur
Suivant .

- nommer la classe QuotesViewController
- faire un héritier de NSViewController
- assurez-vous que la case à cocher Créer également un fichier XIB pour l'interface utilisateur n'est pas cochée
- définir la langue sur Swift
Enfin, cliquez Ă nouveau sur
Suivant , sélectionnez un emplacement pour enregistrer le fichier, puis cliquez sur
Créer .
Maintenant, ouvrez
Main.storyboard . Développez
View Controller Scene et sélectionnez
View Controller instance .

Sélectionnez d'abord l'
inspecteur d'identité et changez la classe en
QuotesViewController , puis définissez l'
ID du
storyboard sur
QuotesViewControllerAjoutez maintenant le code suivant Ă la fin du fichier
QuotesViewController.swift :
extension QuotesViewController {
Que se passe-t-il ici:
- nous obtenons un lien vers Main.storyboard .
- créer un identifiant de scène qui correspond à celui que nous venons d'installer juste au-dessus.
- créez une instance de QuotesViewController et renvoyez-la.
Vous créez cette méthode, donc maintenant, tous ceux qui utilisent le
QuotesViewController n'ont pas besoin de savoir comment il est créé. Ça marche juste.
Notez le
fatalError à l'intérieur de l'instruction
guard . Il peut être agréable de l'utiliser ou d'
assertionFailure de sorte que si quelque chose se passe mal dans le développement, vous-même et les autres membres de l'équipe de développement soyez au courant.
Revenons maintenant Ă
AppDelegate.swift . Ajoutez une nouvelle propriété.
let popover = NSPopover()
Remplacez ensuite un
pplicationDidFinishLaunching (_ :) par le code suivant:
func applicationDidFinishLaunching(_ aNotification: Notification) { if let button = statusItem.button { button.image = NSImage(named:NSImage.Name("StatusBarButtonImage")) button.action = #selector(togglePopover(_:)) } popover.contentViewController = QuotesViewController.freshController() }
Vous avez modifié l'action de clic pour appeler la
méthode togglePopover (_ :) , que nous écrirons un peu plus tard. De plus, au lieu de configurer et d'ajouter un menu, nous avons configuré une fenêtre contextuelle qui affichera quelque chose de
QuotesViewController .
Ajoutez les trois mĂ©thodes suivantes Ă
AppDelegate :
@objc func togglePopover(_ sender: Any?) { if popover.isShown { closePopover(sender: sender) } else { showPopover(sender: sender) } } func showPopover(sender: Any?) { if let button = statusItem.button { popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY) } } func closePopover(sender: Any?) { popover.performClose(sender) }
showPopover () affiche une fenêtre contextuelle. Vous indiquez simplement d'où il vient, macOS le positionne et dessine une flèche, comme s'il apparaissait dans la barre de menus.
closePopover () ferme simplement la fenĂŞtre contextuelle et
togglePopover () est une méthode qui affiche ou masque la fenêtre contextuelle, selon son état.
Lancez l'application et cliquez sur son icĂ´ne.

Tout va bien, mais oĂą est le contenu?
Nous implémentons Quote View Controller
Vous avez d'abord besoin d'un modèle pour stocker les citations et les attributs. Allez dans le menu
Fichier / Nouveau / Fichier ... et sélectionnez le
modèle de fichier macOS / Source / Swift , puis
Suivant . Nommez le fichier
Quote et cliquez sur
Créer .
Ouvrez le fichier
Quote.swift et ajoutez-y le code suivant:
struct Quote { let text: String let author: String static let all: [Quote] = [ Quote(text: "Never put off until tomorrow what you can do the day after tomorrow.", author: "Mark Twain"), Quote(text: "Efficiency is doing better what is already being done.", author: "Peter Drucker"), Quote(text: "To infinity and beyond!", author: "Buzz Lightyear"), Quote(text: "May the Force be with you.", author: "Han Solo"), Quote(text: "Simplicity is the ultimate sophistication", author: "Leonardo da Vinci"), Quote(text: "It's not just what it looks like and feels like. Design is how it works.", author: "Steve Jobs") ] } extension Quote: CustomStringConvertible { var description: String { return "\"\(text)\" — \(author)" } }
Ici, nous définissons une structure de devis simple et une propriété statique qui renvoie tous les devis. Puisque nous avons rendu
Quote compatible avec le protocole
CustomStringConvertible , nous pouvons facilement obtenir du texte formaté de manière pratique.
Il y a des progrès, mais nous avons encore besoin de contrôles pour afficher tout cela.
Ajouter des éléments d'interface
Ouvrez le tableau principal et retirez 3 boutons (
bouton- poussoir) et l'étiquette (étiquette
multiligne) sur le contrĂ´leur de vue.
Positionnez les boutons et l'étiquette de manière à ce qu'ils ressemblent à ceci:

Fixez le bouton gauche au bord gauche avec un écart de 20 et centrez verticalement.
Fixez le bouton droit au bord droit avec un écart de 20 et centrez verticalement.
Fixez le bouton inférieur au bord inférieur avec un espace de 20 et centrez horizontalement.
Fixez les bords gauche et droit de la marque aux boutons avec un espace de 20, centrez verticalement.

Vous verrez plusieurs erreurs de mise en page, car il n'y a pas suffisamment d'informations pour que
la mise en
page automatique puisse le comprendre.
Définissez la
Priorité d'étirement du contenu horizontal sur 249 pour permettre à l'étiquette de se redimensionner.

Procédez maintenant comme suit:
- définissez l' image du bouton gauche sur NSGoLeftTemplate et effacez le titre
- définissez l' image du bouton droit sur NSGoRightTemplate et effacez le titre
- définissez le titre du bouton ci-dessous sur Quitter les devis .
- définissez l' alignement du texte de l'étiquette au centre.
- vérifiez que le saut de ligne sur l'étiquette est défini sur Retour à la ligne .
Ouvrez maintenant
QuotesViewController.swift et ajoutez le code suivant à l'implémentation de la classe
QuotesViewController :
@IBOutlet var textLabel: NSTextField!
Ajoutez cette extension à l'implémentation de classe. Maintenant, dans
QuotesViewController.swift, il y a deux extensions de classe.
Nous venons d'ajouter une
sortie pour l'étiquette que nous utiliserons pour afficher les citations et 3 méthodes de stub que nous connecterons avec les boutons.
Connexion du code avec Interface Builder
Remarque: Xcode a placé des cercles à gauche de votre code - à côté des mots clés
IBAction et
IBOutlet .

Nous les utiliserons pour connecter le code Ă l'interface utilisateur.
Tout en maintenant la touche
alt enfoncée, cliquez sur
Main.storyboard dans le
navigateur de projet . Ainsi, le
storyboard s'ouvre dans l'
Assistant Editor Ă droite et le code Ă gauche.
Faites glisser le cercle Ă gauche de
textLabel sur l'étiquette dans le
générateur d'interface . De la même manière, combinez les méthodes
précédente ,
suivante et
quitter avec les boutons gauche, droit et bas, respectivement.

Lancez votre application.

Nous avons utilisé la taille du popup par défaut. Si vous voulez un popup plus grand ou plus petit, redimensionnez-le simplement dans le
storyboard .
Écrire un code pour les boutons
Si vous n'avez pas déjà masqué l'
Editeur Assistant , cliquez sur
Cmd-Retour ou
Vue> Editeur standard> Afficher l'éditeur standardOuvrez
QuotesViewController.swift et ajoutez les propriétés suivantes à l'implémentation de classe:
let quotes = Quote.all var currentQuoteIndex: Int = 0 { didSet { updateQuote() } }
La propriété
quotes contient toutes les citations et
currentQuoteIndex est l'index de la citation actuellement affichée.
CurrentQuoteIndex possède également un
observateur de propriétés pour mettre à jour le contenu de l'étiquette avec un nouveau devis lorsque l'index change.
Ajoutez maintenant les méthodes suivantes:
override func viewDidLoad() { super.viewDidLoad() currentQuoteIndex = 0 } func updateQuote() { textLabel.stringValue = String(describing: quotes[currentQuoteIndex]) }
Lorsque la vue se charge, nous définissons l'index des devis à 0, ce qui entraîne une mise à jour de l'interface.
updateQuote () met simplement à jour l'étiquette de texte pour afficher un devis.
currentQuoteIndex correspondant.
Enfin, mettez à jour ces méthodes avec le code suivant:
@IBAction func previous(_ sender: NSButton) { currentQuoteIndex = (currentQuoteIndex - 1 + quotes.count) % quotes.count } @IBAction func next(_ sender: NSButton) { currentQuoteIndex = (currentQuoteIndex + 1) % quotes.count } @IBAction func quit(_ sender: NSButton) { NSApplication.shared.terminate(sender) }
Les méthodes
next () et
previous () parcourent toutes les citations.
quitter ferme l'application.
Lancez l'application:

Suivi des événements
Il y a encore une chose que les utilisateurs attendent de notre application: masquer la fenêtre contextuelle lorsque l'utilisateur clique quelque part en dehors de celle-ci. Pour ce faire, nous avons besoin d'un mécanisme appelé
moniteur d'événements global macOS .
Créez un nouveau fichier Swift, appelez-le
EventMonitor et remplacez son contenu par le code suivant:
import Cocoa public class EventMonitor { private var monitor: Any? private let mask: NSEvent.EventTypeMask private let handler: (NSEvent?) -> Void public init(mask: NSEvent.EventTypeMask, handler: @escaping (NSEvent?) -> Void) { self.mask = mask self.handler = handler } deinit { stop() } public func start() { monitor = NSEvent.addGlobalMonitorForEvents(matching: mask, handler: handler) } public func stop() { if monitor != nil { NSEvent.removeMonitor(monitor!) monitor = nil } } }
Lors de l'initialisation d'une instance de cette classe, nous lui transmettons un masque d'événement que nous écouterons (comme les frappes de touches, les molettes de la souris, etc.) et un gestionnaire d'événements.
Lorsque nous sommes prêts à commencer à écouter,
start () appelle
addGlobalMonitorForEventsMatchingMask (_: handler :) , qui renvoie l'objet que nous enregistrons. Dès que l'événement contenu dans le masque se produit, le système appelle votre gestionnaire.
Pour arrêter la surveillance des événements,
removeMonitor () est appelé dans
stop () et nous supprimons l'objet en le
définissant sur nil.
Il ne nous reste plus qu'Ă appeler
start () et
stop () au bon moment. La classe appelle également
stop () sur le deinitializer pour nettoyer.
Connexion du moniteur d'événements
Ouvrez
AppDelegate.swift une dernière fois et ajoutez une nouvelle propriété:
var eventMonitor: EventMonitor?
Ajoutez ensuite ce code pour configurer le
moniteur d'événements à la fin de
applicationDidFinishLaunching (_ :) eventMonitor = EventMonitor(mask: [.leftMouseDown, .rightMouseDown]) { [weak self] event in if let strongSelf = self, strongSelf.popover.isShown { strongSelf.closePopover(sender: event) } }
Cela informera votre application lorsque vous cliquez sur le bouton gauche ou droit. Veuillez noter: le gestionnaire ne sera pas appelé en réponse aux clics de souris dans votre application. C'est pourquoi la fenêtre contextuelle ne se fermera pas lorsque vous cliquez à l'intérieur.
Nous utilisons une référence
faible Ă
soi pour éviter le danger d'un cycle de liens solides entre
AppDelegate et
EventMonitor .
Ajoutez le code suivant Ă la fin de la
méthode showPopover (_ :) :
eventMonitor?.start()
Ici, nous commençons à surveiller les événements lorsqu'une fenêtre contextuelle apparaît.
Ajoutez maintenant le code Ă la fin de la
méthode closePopover (_ :) :
eventMonitor?.stop()
Ici, nous terminons la surveillance lorsque le popup se ferme.
L'application est prĂŞte!
Conclusion
Vous trouverez ici le code complet de ce projet.
Vous avez appris à configurer le menu et la fenêtre contextuelle dans l'application située dans la barre de menus. Pourquoi ne pas essayer plusieurs balises ou du texte formaté pour mieux voir les citations? Ou connectez-vous à un backend pour recevoir des devis d'Internet? Ou voulez-vous utiliser le clavier pour naviguer entre les guillemets?
Un bon endroit pour rechercher est la documentation officielle:
NSMenu ,
NSPopover et
NSStatusItem .