Os aplicativos localizados na barra de menus são conhecidos há muito tempo pelos usuários do macOS. Alguns desses aplicativos têm uma parte "normal", outros estão localizados apenas na barra de menus.
Neste guia, você escreverá um aplicativo que exibe várias citações de pessoas famosas em uma janela pop-up. No processo de criação deste aplicativo, você aprenderá:
- atribuir ícone do aplicativo na barra de menus
- torne o aplicativo hospedado apenas na barra de menus
- adicionar menu personalizado
- mostre uma janela pop-up a pedido do usuário e oculte-a quando necessário, usando o Monitoramento de Eventos
Nota: este guia pressupõe que você esteja familiarizado com o Swift e o macOS.
Introdução
Inicie o Xcode. Em seguida, no menu 
Arquivo / Novo / Projeto ... , selecione o modelo 
macOS / Application / Cocoa App e clique em 
Avançar .
Na próxima tela, insira Cotações como o 
Nome do 
produto , selecione o 
Nome da organização e o 
Identificador da organização . Em seguida, verifique se Swift está selecionado como idioma do aplicativo e a caixa de seleção 
Usar Storyboards está marcada. Desmarque as 
caixas de seleção Criar aplicativo baseado em documento , 
Usar dados principais , 
Incluir testes de unidade e 
Incluir testes de interface do usuário .

Por fim, clique em 
Avançar novamente, especifique o local para salvar o projeto e clique em 
Criar .
Depois que o novo projeto for criado, abra 
AppDelegate.swift e adicione a seguinte propriedade à classe:
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength) 
Aqui, criamos na barra de menus Item de status (ícone do aplicativo) um comprimento fixo que ficará visível para os usuários.
Em seguida, precisamos atribuir nossa imagem a esse novo item na barra de menus para que possamos distinguir nosso novo aplicativo.
No navegador do projeto, acesse Assets.xcassets, 
faça upload de uma imagem e arraste-a para o catálogo de ativos.
Selecione uma imagem e abra o inspetor de atributos. Altere a opção 
Renderizar como para 
Imagem do modelo .

Se você usar sua própria imagem, verifique se a imagem é em preto e branco e configure-a como uma 
imagem de modelo para que o ícone fique ótimo nas barras de menu escura e clara.
Volte para 
AppDelegate.swift e adicione o seguinte código ao 
applicationDidFinishLaunching (_ :) if let button = statusItem.button { button.image = NSImage(named:NSImage.Name("StatusBarButtonImage")) button.action = #selector(printQuote(_:)) } 
Aqui, atribuímos o ícone do aplicativo que acabamos de adicionar ao ícone do aplicativo e atribuímos uma 
ação quando clicamos nele.
Adicione o seguinte método à 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)") } 
Este método simplesmente imprime a cotação no console.
Preste atenção à 
diretiva do método 
objc . Isso permite que você use esse método como resposta a um clique no botão.
Crie e execute o aplicativo e você verá o novo aplicativo na barra de menus. Viva!
Cada vez que você clica no ícone na barra de menus, o famoso ditado de Mark Twain é exibido no console do Xcode.
Escondemos a janela principal e o ícone no banco dos réus
Há algumas pequenas coisas que precisamos fazer antes de lidar diretamente com a funcionalidade:
- excluir ícone de encaixe
- remover a janela principal do aplicativo desnecessário
Para remover o ícone da estação, abra 
Info.plist . Adicione uma nova chave 
Application is agent (UIElement) e defina seu valor como 
YES .

Agora é a hora de lidar com a janela principal do aplicativo.
- abra Main.storyboard
- selecione a cena Window Controller e exclua-a
- Ver a cena da controladora sair, usaremos em breve

Crie e execute o aplicativo. Agora, o aplicativo não possui a janela principal e o ícone desnecessário no dock. Ótimo!
Adicionar menu ao item de status
Uma resposta de clique único claramente não é suficiente para um aplicativo sério. A maneira mais fácil de adicionar funcionalidade é adicionar um menu. Adicione esta função no final do 
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 } 
E adicione essa chamada no final do 
applicationDidFinishLaunching (_ :) constructMenu() 
Criamos o 
NSMenu , adicionamos 3 instâncias do 
NSMenuItem a ele e definimos esse menu como o menu do ícone do aplicativo.
Alguns pontos importantes:
- O título do item de menu é o texto que aparece no menu. Um bom lugar para localizar o aplicativo (se necessário).
- ação , como a ação de um botão ou outro controle, é um método chamado quando o usuário clica em um item de menu
- keyEquivalent é um atalho de teclado que você pode usar para selecionar um item de menu. Caracteres minúsculos usam Cmd como um modificador e caracteres minúsculos usam Cmd + Shift . Isso só funciona se o aplicativo estiver no topo e estiver ativo. No nosso caso, é necessário que o menu ou alguma outra janela esteja visível, pois nosso aplicativo não possui um ícone no dock
- separatorItem é um item de menu inativo na forma de uma linha cinza entre outros elementos. Use-o para agrupar
- printQuote é o método que você já definiu no AppDelegate e termina é o método definido pelo NSApplication .
Inicie o aplicativo e você verá um menu clicando no ícone do aplicativo.

Tente clicar no menu - selecionar 
Imprimir cotação exibe a cotação no console do Xcode e 
Quit Quote encerra o aplicativo.
Adicionar um pop-up
Você viu como é fácil adicionar um menu a partir do código, mas exibir uma cotação no console do Xcode claramente não é o que os usuários esperam do aplicativo. Agora vamos adicionar um controlador de exibição simples para exibir as cotações da maneira correta.
Vá para o menu 
Arquivo / Novo / Arquivo ... , selecione o modelo 
macOS / Source / Cocoa Class e clique em 
Avançar .

- nomeie a classe QuotesViewController
- tornar um herdeiro do NSViewController
- verifique se a caixa de seleção Também criar arquivo XIB para a interface do usuário não está marcada
- defina o idioma para Swift
Por fim, clique em 
Avançar novamente, selecione um local para salvar o arquivo e clique em 
Criar .
Agora abra 
Main.storyboard . Expanda 
Exibir cena do controlador e selecione 
Exibir instância do controlador .

Primeiro, selecione o 
Identity Inspector e altere a classe para 
QuotesViewController , depois defina o 
Storyboard ID como 
QuotesViewControllerAgora adicione o seguinte código ao final do arquivo 
QuotesViewController.swift :
 extension QuotesViewController {  
O que está acontecendo aqui:
- nós temos um link para Main.storyboard .
- crie um identificador de cena que corresponda ao que acabamos de instalar logo acima.
- crie uma instância de QuotesViewController e retorne-a.
Como você cria esse método, agora todos que usam o 
QuotesViewController não precisam saber como ele é criado. Isso simplesmente funciona.
Observe o 
fatalError dentro da declaração de 
guarda . Pode ser bom usá-lo ou 
assertionFailure para que, se algo no desenvolvimento der errado, você e os outros membros da equipe de desenvolvimento fiquem sabendo.
Agora, de volta ao 
AppDelegate.swift . Adicione uma nova propriedade.
 let popover = NSPopover() 
Em seguida, substitua um 
pplicationDidFinishLaunching (_ :) pelo seguinte 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() } 
Você alterou a ação de clique para chamar o 
método togglePopover (_ :) , que escreveremos um pouco mais tarde. Além disso, em vez de configurar e adicionar um menu, configuramos uma janela pop-up que mostrará algo do 
QuotesViewController .
Adicione os três métodos a 
seguir ao 
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 () mostra um pop-up. Você apenas indica de onde vem, o macOS posiciona-o e desenha uma seta, como se aparecesse na barra de menus.
closePopover () apenas fecha o pop-up e 
togglePopover () é um método que mostra ou oculta o pop-up, dependendo do seu estado.
Inicie o aplicativo e clique no ícone.

Está tudo bem, mas onde está o conteúdo?
Implementamos o Quote View Controller
Primeiro, você precisa de um modelo para armazenar cotações e atributos. Vá para o menu 
Arquivo / Novo / Arquivo ... e selecione o modelo 
macOS / Source / Swift File e , em seguida, 
Avançar . Nomeie o arquivo como 
Quote e clique em 
Create .
Abra o arquivo 
Quote.swift e adicione o seguinte 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)" } } 
Aqui, definimos uma estrutura de cotação simples e uma propriedade estática que retorna todas as cotações. Como tornamos o 
Quote compatível com o protocolo 
CustomStringConvertible , podemos facilmente obter texto formatado de maneira conveniente.
Há progresso, mas ainda precisamos de controles para exibir tudo isso.
Adicionar elementos da interface
Abra Main.storyboard e puxe 3 botões ( 
Push Button ) e a etiqueta ( 
Multiline Label) no controlador de exibição.
Posicione os botões e o rótulo para que eles se pareçam com isso:

Anexe o botão esquerdo à borda esquerda com um espaço de 20 e centralize verticalmente.
Anexe o botão direito à borda direita com um espaço de 20 e centralize verticalmente.
Anexe o botão inferior à borda inferior com um espaço de 20 e centralize horizontalmente.
Anexe as bordas esquerda e direita da marca aos botões com um espaço de 20, centralmente na vertical.

Você verá vários erros de layout, pois não há informações suficientes para o 
layout automático descobrir.
Defina a 
Prioridade de abraço de conteúdo horizontal como 249 para permitir que o rótulo seja redimensionado.

Agora faça o seguinte:
- defina a imagem do botão esquerdo como NSGoLeftTemplate e limpe o título
- defina a imagem do botão direito como NSGoRightTemplate e limpe o título
- defina o título do botão abaixo como Quit Quotes .
- defina o alinhamento do texto da etiqueta para centralizar.
- verifique se a quebra de linha no rótulo está definida como quebra automática de linha .
Agora abra 
QuotesViewController.swift e adicione o seguinte código à implementação da classe 
QuotesViewController :
 @IBOutlet var textLabel: NSTextField! 
Adicione esta extensão à implementação da classe. Agora, em 
QuotesViewController.swift, existem duas extensões de classe.
 
Acabamos de adicionar uma 
saída para a etiqueta que usaremos para exibir aspas e três métodos de stub que conectaremos com os botões.
Conectando o código ao Interface Builder
Nota: O Xcode colocou círculos à esquerda do seu código - ao lado das palavras-chave 
IBAction e 
IBOutlet .

Vamos usá-los para conectar o código à interface do usuário.
Enquanto mantém pressionada a tecla 
alt , clique em 
Main.storyboard no 
navegador do 
projeto . Assim, o 
storyboard é aberto no 
Assistente do Editor à direita e o código à esquerda.
Arraste o círculo à esquerda do 
textLabel para o rótulo no 
construtor de interfaces . Da mesma forma, combine os métodos 
anterior , 
próximo e 
sair com os botões esquerdo, direito e inferior, respectivamente.

Inicie seu aplicativo.

Usamos o tamanho padrão do pop-up. Se você quiser um pop-up maior ou menor, redimensione-o no 
storyboard .
Escrevendo um código para os botões
Se você ainda não ocultou o 
Editor Assistente , clique em 
Cmd-Return ou 
View> Editor Padrão> Mostrar Editor PadrãoAbra 
QuotesViewController.swift e adicione as seguintes propriedades à implementação da classe:
 let quotes = Quote.all var currentQuoteIndex: Int = 0 { didSet { updateQuote() } } 
A propriedade 
aspas contém todas as aspas, e 
currentQuoteIndex é o índice da cotação que está sendo exibida no momento. 
CurrentQuoteIndex também possui um 
observador de propriedades para atualizar o conteúdo do rótulo com uma nova cotação quando o índice for alterado.
Agora adicione os seguintes métodos:
 override func viewDidLoad() { super.viewDidLoad() currentQuoteIndex = 0 } func updateQuote() { textLabel.stringValue = String(describing: quotes[currentQuoteIndex]) } 
Quando a visualização é carregada, definimos o índice de cotação como 0, o que, por sua vez, leva a uma atualização da interface. 
updateQuote () simplesmente atualiza o rótulo do texto para exibir uma cotação. 
currentQuoteIndex correspondente.
Por fim, atualize esses métodos com o seguinte 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) } 
Os métodos 
next () e 
previous () alternam entre todas as citações. 
sair fecha o aplicativo.
Inicie o aplicativo:

Monitoramento de eventos
Há mais uma coisa que os usuários esperam do nosso aplicativo: ocultar a janela pop-up quando o usuário clicar em algum lugar fora dela. Para fazer isso, precisamos de um mecanismo chamado 
macOS global event monitor .
Crie um novo arquivo Swift, chame-o de 
EventMonitor e substitua seu conteúdo pelo seguinte 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 } } } 
Ao inicializar uma instância dessa classe, passamos a ela uma máscara de evento que iremos ouvir (como pressionamentos de teclas, rolagens da roda do mouse etc.) e um manipulador de eventos.
Quando estivermos prontos para começar a ouvir, 
start () chama 
addGlobalMonitorForEventsMatchingMask (_: handler :) , que retorna o objeto que estamos salvando. Assim que o evento contido na máscara acontece, o sistema chama seu manipulador.
Para parar o monitoramento de eventos, 
removeMonitor () é chamado em 
stop () e excluímos o objeto, 
definindo -o como nulo.
Tudo o que resta para nós é chamar 
start () e 
stop () na hora certa. A classe também chama 
stop () no desinicializador para limpar.
Conectando o Monitor de Eventos
Abra 
AppDelegate.swift uma última vez e adicione uma nova propriedade:
 var eventMonitor: EventMonitor? 
Em seguida, inclua esse código para configurar o 
monitor de eventos no final do 
applicationDidFinishLaunching (_ :) eventMonitor = EventMonitor(mask: [.leftMouseDown, .rightMouseDown]) { [weak self] event in if let strongSelf = self, strongSelf.popover.isShown { strongSelf.closePopover(sender: event) } } 
Isso informará seu aplicativo quando você clicar no botão esquerdo ou direito. Observe: o manipulador não será chamado em resposta aos cliques do mouse dentro do seu aplicativo. É por isso que o pop-up não fecha enquanto você clica dentro dele.
Usamos uma referência 
fraca a 
si próprio para evitar o perigo de um ciclo de vínculos fortes entre 
AppDelegate e 
EventMonitor .
Adicione o seguinte código no final do método 
showPopover (_ :) :
 eventMonitor?.start() 
Aqui começamos a monitorar eventos quando uma janela pop-up aparece.
Agora adicione o código no final do método 
closePopover (_ :) :
 eventMonitor?.stop() 
Aqui terminamos o monitoramento quando o pop-up é fechado.
A aplicação está pronta!
Conclusão
Aqui você encontrará o código completo para este projeto.
Você aprendeu como definir o menu e pop-up no aplicativo localizado na barra de menus. Por que não experimentar várias tags ou texto formatado para obter uma melhor aparência das aspas? Ou conectar um back-end para receber cotações da Internet? Ou você deseja usar o teclado para navegar entre aspas?
Um bom lugar para pesquisar é a documentação oficial: 
NSMenu , 
NSPopover e 
NSStatusItem .