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 .