Las aplicaciones ubicadas en la barra de menú han sido conocidas por los usuarios de macOS. Algunas de estas aplicaciones tienen una parte "normal", otras se encuentran solo en la barra de menú.
En esta guía, escribirá una aplicación que muestra varias citas de personas famosas en una ventana emergente. En el proceso de creación de esta aplicación, aprenderá:
- asignar icono de la aplicación en la barra de menú
- hacer que la aplicación esté alojada solo en la barra de menú
- agregar menú personalizado
- muestra una ventana emergente a solicitud del usuario y la oculta cuando es necesario, utilizando Event Monitoring
Nota: esta guía asume que está familiarizado con Swift y macOS.
Empezando
Lanzamiento de Xcode. A continuación, en el menú 
Archivo / Nuevo / Proyecto ... , seleccione la plantilla 
macOS / Aplicación / Aplicación de Cocoa y haga clic en 
Siguiente .
En la siguiente pantalla, ingrese Cotizaciones como el 
Nombre del 
producto , seleccione su 
Nombre de organización e 
Identificador de organización . Luego, asegúrese de que Swift esté seleccionado como el idioma de la aplicación y que la casilla de verificación 
Usar guiones gráficos esté marcada. Desmarque las 
casillas de verificación Crear aplicación basada en documentos , 
Usar datos básicos , 
Incluir pruebas unitarias e 
Incluir pruebas de IU .

Finalmente, haga clic en 
Siguiente nuevamente, especifique la ubicación para guardar el proyecto y haga clic en 
Crear .
Una vez que se crea el nuevo proyecto, abra 
AppDelegate.swift y agregue la siguiente propiedad a la clase:
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength) 
Aquí creamos en la barra de menú Elemento de estado (icono de la aplicación) una longitud fija que será visible para los usuarios.
Luego, debemos asignar nuestra imagen a este nuevo elemento en la barra de menú para que podamos distinguir nuestra nueva aplicación.
En el navegador de proyectos, vaya a Assets.xcassets, 
cargue una imagen y arrástrela al catálogo de activos.
Seleccione una imagen y abra el inspector de atributos. Cambie la opción 
Renderizar como a 
Imagen de plantilla .

Si usa su propia imagen, asegúrese de que la imagen sea en blanco y negro y configúrela como una 
imagen de plantilla para que el icono se vea bien tanto en la barra de menú oscura como en la clara.
Regrese a 
AppDelegate.swift y agregue el siguiente código a 
applicationDidFinishLaunching (_ :) if let button = statusItem.button { button.image = NSImage(named:NSImage.Name("StatusBarButtonImage")) button.action = #selector(printQuote(_:)) } 
Aquí asignamos el icono de la aplicación que acabamos de agregar al icono de la aplicación y asignamos 
acción cuando hacemos clic en él.
Agregue el siguiente método a la clase:
 @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)") } 
Este método simplemente imprime la cita en la consola.
Presta atención a la 
directiva del método 
objc . Esto le permite utilizar este método como respuesta a un clic de botón.
Compile y ejecute la aplicación, y verá la nueva aplicación en la barra de menú. ¡Hurra!
Cada vez que hace clic en el icono en la barra de menú, el famoso dicho de Mark Twain se muestra en la consola Xcode.
Ocultamos la ventana principal y el ícono en el dock
Hay un par de pequeñas cosas que debemos hacer antes de tratar directamente con la funcionalidad:
- eliminar icono del muelle
- eliminar la ventana principal de la aplicación innecesaria
Para eliminar el icono del dock, abra 
Info.plist . Agregue una nueva clave 
Aplicación es agente (UIElement) y establezca su valor en 
SÍ .

Ahora es el momento de lidiar con la ventana principal de la aplicación.
- abrir Main.storyboard
- seleccione la escena del controlador de ventana y elimínela
- Ver la escena del controlador , la usaremos pronto

Compila y ejecuta la aplicación. Ahora la aplicación no tiene tanto la ventana principal como el icono innecesario en el dock. Genial
Agregar menú al elemento de estado
Una respuesta de un solo clic claramente no es suficiente para una aplicación seria. La forma más fácil de agregar funcionalidad es agregar un menú. Agregue esta función al final de 
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 } 
Y luego agregue esta llamada al final de 
applicationDidFinishLaunching (_ :) constructMenu() 
Creamos 
NSMenu , le agregamos 3 instancias de 
NSMenuItem y configuramos este menú como el menú del icono de la aplicación.
Algunos puntos importantes:
- El título del elemento del menú es el texto que aparece en el menú. Un buen lugar para localizar la aplicación (si es necesario).
- La acción , como la acción de un botón u otro control, es un método que se llama cuando el usuario hace clic en un elemento del menú
- keyEquivalent es un método abreviado de teclado que puede usar para seleccionar un elemento del menú. Los caracteres en minúscula usan Cmd como modificador, y los caracteres en minúscula usan Cmd + Shift . Esto solo funciona si la aplicación está en la parte superior y está activa. En nuestro caso, es necesario que el menú o alguna otra ventana esté visible, ya que nuestra aplicación no tiene un ícono en el dock
- separatorItem es un elemento de menú inactivo en forma de una línea gris entre otros elementos. Úselo para agrupar
- printQuote es el método que ya definió en AppDelegate , y terminar es el método definido por NSApplication .
Inicie la aplicación y verá un menú haciendo clic en el icono de la aplicación.

Intente hacer clic en el menú: al seleccionar 
Imprimir cotización, se muestra la cotización en la consola de Xcode y se cierra la aplicación.
Agregar una ventana emergente
Viste lo fácil que es agregar un menú desde el código, pero mostrar una cita en la consola Xcode claramente no es lo que los usuarios esperan de la aplicación. Ahora agregaremos un controlador de vista simple para mostrar las cotizaciones de la manera correcta.
Vaya al menú 
Archivo / Nuevo / Archivo ... , seleccione la plantilla 
macOS / Source / Cocoa Class y haga clic en 
Siguiente .

- nombrar la clase QuotesViewController
- hacer un heredero de NSViewController
- asegúrese de que la casilla de verificación Crear también archivo XIB para la interfaz de usuario no esté marcada
- establecer el idioma en Swift
Finalmente, haga clic en 
Siguiente nuevamente, seleccione una ubicación para guardar el archivo y haga clic en 
Crear .
Ahora abra 
Main.storyboard . Expanda 
Ver escena del controlador y seleccione 
Ver instancia del controlador .

Primero seleccione el 
Inspector de identidad y cambie la clase a 
QuotesViewController , luego establezca el 
ID del 
guión gráfico en 
QuotesViewControllerAhora agregue el siguiente código al final del archivo 
QuotesViewController.swift :
 extension QuotesViewController {  
¿Qué está pasando aquí?
- obtenemos un enlace a Main.storyboard .
- cree un identificador de escena que coincida con el que acabamos de instalar justo arriba.
- cree una instancia de QuotesViewController y devuélvala.
Usted crea este método, por lo que ahora todos los que usan 
QuotesViewController no necesitan saber cómo se crea. Simplemente funciona
Tenga en cuenta el 
error fatal dentro de la declaración de 
guardia . Puede ser bueno usarlo o 
afirmar Falla para que si algo en el desarrollo sale mal, usted y los otros miembros del equipo de desarrollo estén informados.
Ahora de vuelta a 
AppDelegate.swift . Agregar una nueva propiedad.
 let popover = NSPopover() 
Luego reemplace un 
pplicationDidFinishLaunching (_ :) con el siguiente código:
 func applicationDidFinishLaunching(_ aNotification: Notification) { if let button = statusItem.button { button.image = NSImage(named:NSImage.Name("StatusBarButtonImage")) button.action = #selector(togglePopover(_:)) } popover.contentViewController = QuotesViewController.freshController() } 
Ha cambiado la acción de hacer clic para llamar al 
método togglePopover (_ :) , que escribiremos un poco más tarde. Además, en lugar de configurar y agregar un menú, configuramos una ventana emergente que mostrará algo de 
QuotesViewController .
Agregue los siguientes tres métodos a 
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 () muestra una ventana emergente. Simplemente indica de dónde viene, macOS lo posiciona y dibuja una flecha, como si apareciera en la barra de menú.
closePopover () simplemente cierra la ventana emergente, y 
togglePopover () es un método que muestra u oculta la ventana emergente, dependiendo de su estado.
Inicie la aplicación y haga clic en su icono.

Todo está bien, pero ¿dónde está el contenido?
Implementamos Quote View Controller
Primero necesita un modelo para almacenar citas y atributos. Vaya al menú 
Archivo / Nuevo / Archivo ... y seleccione la 
plantilla macOS / Fuente / Archivo Swift , luego 
Siguiente . Asigne un nombre al archivo 
Cita y haga clic en 
Crear .
Abra el archivo 
Quote.swift y agregue el siguiente código:
 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)" } } 
Aquí definimos una estructura de cotización simple y una propiedad estática que devuelve todas las cotizaciones. Dado que hicimos que 
Quote cumpliera con el protocolo 
CustomStringConvertible , podemos obtener fácilmente un texto con formato conveniente.
Hay progreso, pero aún necesitamos controles para mostrar todo esto.
Agregar elementos de interfaz
Abra Main.storyboard y extraiga 3 botones ( 
Push Button ) y etiquete ( 
Multiline Label) en el controlador de vista.
Coloque los botones y la etiqueta para que se vean así:

Adjunte el botón izquierdo al borde izquierdo con un espacio de 20 y centre verticalmente.
Adjunte el botón derecho al borde derecho con un espacio de 20 y centre verticalmente.
Coloque el botón inferior en el borde inferior con un espacio de 20 y céntrelo horizontalmente.
Adjunte los bordes izquierdo y derecho de la marca a los botones con un espacio de 20, centrado verticalmente.

Verá varios errores de diseño, ya que no hay suficiente información para que el 
diseño automático pueda resolverlo.
Establezca la 
Prioridad de abrazo de contenido horizontal en 249 para permitir que la etiqueta cambie de tamaño.

Ahora haga lo siguiente:
- establece la imagen del botón izquierdo en NSGoLeftTemplate y borra el título
- establezca la imagen del botón derecho en NSGoRightTemplate y borre el título
- configure el título del botón de abajo para Salir de las cotizaciones .
- establece la alineación del texto de la etiqueta al centro.
- verifique que el Salto de línea en la etiqueta esté configurado en Ajuste de línea .
Ahora abra 
QuotesViewController.swift y agregue el siguiente código a la implementación de la clase 
QuotesViewController :
 @IBOutlet var textLabel: NSTextField! 
Agregue esta extensión a la implementación de la clase. Ahora en 
QuotesViewController.swift hay dos extensiones de clase.
 
Acabamos de agregar una 
salida para la etiqueta que usaremos para mostrar las comillas, y 3 métodos de código auxiliar que conectaremos con los botones.
Conectando el código con Interface Builder
Nota: Xcode ha colocado círculos a la izquierda de su código, junto a las palabras clave 
IBAction e 
IBOutlet .

Los usaremos para conectar el código a la interfaz de usuario.
Mientras mantiene presionada la tecla 
alt , haga clic en 
Main.storyboard en el 
navegador de proyectos . Por lo tanto, el 
guión gráfico se abre en el 
Editor Asistente a la derecha y el código a la izquierda.
Arrastre el círculo a la izquierda de 
textLabel a la etiqueta en el 
generador de interfaces . Del mismo modo, combine los métodos 
anterior , 
siguiente y de 
salida con los botones izquierdo, derecho e inferior, respectivamente.

Inicia tu aplicación.

Utilizamos el tamaño emergente predeterminado. Si desea una ventana emergente más grande o más pequeña, simplemente cambie su tamaño en el 
guión gráfico .
Escribir un código para los botones
Si aún no ha ocultado el 
Editor Asistente , haga clic en 
Cmd-Return o V 
iew> Editor estándar> Mostrar editor estándarAbra 
QuotesViewController.swift y agregue las siguientes propiedades a la implementación de la clase:
 let quotes = Quote.all var currentQuoteIndex: Int = 0 { didSet { updateQuote() } } 
La propiedad de 
citas contiene todas las citas, y 
currentQuoteIndex es el índice de la cita que se está mostrando actualmente. 
CurrentQuoteIndex también tiene un 
observador de propiedades para actualizar el contenido de la etiqueta con una nueva cita cuando cambia el índice.
Ahora agregue los siguientes métodos:
 override func viewDidLoad() { super.viewDidLoad() currentQuoteIndex = 0 } func updateQuote() { textLabel.stringValue = String(describing: quotes[currentQuoteIndex]) } 
Cuando se carga la vista, establecemos el índice de cotización en 0, lo que a su vez conduce a una actualización de la interfaz. 
updateQuote () simplemente actualiza la etiqueta de texto para mostrar una cotización. correspondiente 
currentQuoteIndex .
Finalmente, actualice estos métodos con el siguiente código:
 @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) } 
Los métodos 
next () y 
previous () recorren todas las citas. 
Salir cierra la aplicación.
Inicia la aplicación:

Monitoreo de eventos.
Hay una cosa más que los usuarios esperan de nuestra aplicación: ocultar la ventana emergente cuando el usuario hace clic fuera de ella. Para hacer esto, necesitamos un mecanismo llamado 
monitor de eventos globales macOS .
Cree un nuevo archivo Swift, 
llámelo EventMonitor y reemplace su contenido con el siguiente código:
 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 } } } 
Al inicializar una instancia de esta clase, le pasamos una máscara de evento que escucharemos (como pulsaciones de teclas, desplazamiento de la rueda del mouse, etc.) y un controlador de eventos.
Cuando estamos listos para comenzar a escuchar, 
start () llama a 
addGlobalMonitorForEventsMatchingMask (_: handler :) , que devuelve el objeto que estamos guardando. Tan pronto como ocurre el evento contenido en la máscara, el sistema llama a su controlador.
Para detener la supervisión de eventos, se llama a 
removeMonitor () en 
stop () y eliminamos el objeto 
configurándolo en nil.
Todo lo que nos queda es llamar a 
start () y 
stop () en el momento adecuado. La clase también llama a 
stop () en el desinfectante para limpiar.
Conectando el monitor de eventos
Abra 
AppDelegate.swift por última vez y agregue una nueva propiedad:
 var eventMonitor: EventMonitor? 
Luego agregue este código para configurar el 
monitor de eventos al final de 
applicationDidFinishLaunching (_ :) eventMonitor = EventMonitor(mask: [.leftMouseDown, .rightMouseDown]) { [weak self] event in if let strongSelf = self, strongSelf.popover.isShown { strongSelf.closePopover(sender: event) } } 
Esto informará a su aplicación cuando haga clic en el botón izquierdo o derecho. Tenga en cuenta que no se llamará al controlador en respuesta a los clics del mouse dentro de su aplicación. Es por eso que la ventana emergente no se cerrará mientras hace clic dentro de ella.
Utilizamos una referencia 
débil a 
uno mismo para evitar el peligro de un ciclo de fuertes vínculos entre 
AppDelegate y 
EventMonitor .
Agregue el siguiente código al final del método 
showPopover (_ :) :
 eventMonitor?.start() 
Aquí comenzamos a monitorear eventos cuando aparece una ventana emergente.
Ahora agregue el código al final del método 
closePopover (_ :) :
 eventMonitor?.stop() 
Aquí finalizamos el monitoreo cuando se cierra la ventana emergente.
¡La aplicación está lista!
Conclusión
Aquí encontrará el código completo para este proyecto.
Ha aprendido a configurar el menú y la ventana emergente en la aplicación ubicada en la barra de menú. ¿Por qué no experimentar con varias etiquetas o texto formateado para ver mejor las citas? ¿O conecta un backend para recibir cotizaciones de Internet? ¿O quieres usar el teclado para navegar entre comillas?
Un buen lugar para investigar es la documentación oficial: 
NSMenu , 
NSPopover y 
NSStatusItem .