Aplikasi menu bar untuk macOS

Aplikasi yang terletak di bilah menu sudah lama dikenal pengguna macOS. Beberapa aplikasi ini memiliki bagian "normal", yang lain hanya terletak di bilah menu.
Dalam panduan ini, Anda akan menulis aplikasi yang menampilkan beberapa kutipan dari orang-orang terkenal di jendela sembulan. Dalam proses membuat aplikasi ini, Anda akan belajar:

  • menetapkan ikon aplikasi di bilah menu
  • jadikan aplikasi hanya di-host di bilah menu
  • tambahkan menu kustom
  • perlihatkan jendela sembulan atas permintaan pengguna dan sembunyikan jika perlu, menggunakan Pemantauan Acara

Catatan: panduan ini mengasumsikan bahwa Anda terbiasa dengan Swift dan macOS.

Memulai


Luncurkan Xcode. Selanjutnya, pada menu File / New / Project ... , pilih template macOS / Application / Cocoa App dan klik Next .

Pada layar berikutnya, masukkan Kutipan sebagai Nama Produk , pilih Nama Organisasi dan Pengidentifikasi Organisasi Anda . Kemudian pastikan Swift dipilih sebagai bahasa aplikasi dan kotak centang Use Storyboards dicentang. Hapus centang pada Buat Aplikasi Berbasis Dokumen , Gunakan Data Inti , Sertakan tes Unit dan Sertakan kotak centang Tes UI .



Terakhir, klik Next lagi, tentukan lokasi untuk menyimpan proyek dan klik Buat .
Setelah proyek baru dibuat, buka AppDelegate.swift dan tambahkan properti berikut ke kelas:

let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength) 

Di sini kita buat di bilah menu Item Status (ikon aplikasi) panjang tetap yang akan terlihat oleh pengguna.

Maka kita perlu menetapkan gambar kita ke item baru ini di menu bar sehingga kita dapat membedakan aplikasi baru kita.

Di navigator proyek, buka Aset.xcassets, unggah gambar dan seret ke katalog aset.

Pilih gambar dan buka inspektur atribut. Ubah opsi Render As ke Templat Gambar .



Jika Anda menggunakan gambar Anda sendiri, pastikan bahwa gambar itu hitam dan putih dan konfigurasikan sebagai gambar Templat sehingga ikon tampak hebat pada bilah menu gelap dan terang.

Kembali ke AppDelegate.swift , dan tambahkan kode berikut ke applicationDidFinishLaunching (_ :)

 if let button = statusItem.button { button.image = NSImage(named:NSImage.Name("StatusBarButtonImage")) button.action = #selector(printQuote(_:)) } 

Di sini kita menetapkan ikon aplikasi yang baru saja kita tambahkan ke ikon aplikasi dan menetapkan tindakan ketika kita mengkliknya.

Tambahkan metode berikut ke kelas:

 @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)") } 

Metode ini hanya mencetak kutipan ke konsol.

Perhatikan arahan metode objc . Ini memungkinkan Anda menggunakan metode ini sebagai respons terhadap klik tombol.

Bangun dan jalankan aplikasi, dan Anda akan melihat aplikasi baru di bilah menu. Hore!
Setiap kali Anda mengklik ikon di bilah menu, perkataan Mark Twain yang terkenal ditampilkan di konsol Xcode.

Kami menyembunyikan jendela utama dan ikon di dok


Ada beberapa hal kecil yang perlu kita lakukan sebelum berhadapan langsung dengan fungsi:

  • hapus ikon dock
  • hapus jendela utama aplikasi yang tidak perlu

Untuk menghapus ikon dok, buka Info.plist . Tambahkan kunci Application is agent (UIElement) baru dan tetapkan nilainya ke YES .



Sekarang saatnya berurusan dengan jendela aplikasi utama.

  • buka Main.storyboard
  • pilih adegan Window Controller dan hapus
  • Lihat adegan Controller controller , kami akan segera menggunakannya




Bangun dan jalankan aplikasi. Sekarang aplikasi tidak memiliki jendela utama dan ikon yang tidak perlu di dok. Hebat!

Tambahkan menu ke Item Status


Respons satu klik jelas tidak cukup untuk aplikasi serius. Cara termudah untuk menambah fungsionalitas adalah dengan menambahkan menu. Tambahkan fungsi ini di akhir 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 } 

Dan kemudian tambahkan panggilan ini di akhir applicationDidFinishLaunching (_ :)

 constructMenu() 

Kami membuat NSMenu , menambahkan 3 instance NSMenuItem ke dalamnya, dan mengatur menu ini sebagai menu ikon aplikasi.

Beberapa poin penting:

  • Judul item menu adalah teks yang muncul di menu. Tempat yang bagus untuk melokalkan aplikasi (jika perlu).
  • aksi , seperti aksi tombol atau kontrol lainnya, adalah metode yang dipanggil ketika pengguna mengklik item menu
  • keyEquivalent adalah pintasan keyboard yang dapat Anda gunakan untuk memilih item menu. Huruf kecil menggunakan Cmd sebagai pengubah, dan huruf kecil menggunakan Cmd + Shift . Ini hanya berfungsi jika aplikasi berada di paling atas dan aktif. Dalam kasus kami, perlu bahwa menu atau jendela lain terlihat, karena aplikasi kita tidak memiliki ikon di dok
  • separatorItem adalah item menu tidak aktif dalam bentuk garis abu-abu antara elemen lain. Gunakan untuk grup
  • printQuote adalah metode yang sudah Anda tentukan di AppDelegate , dan terminasi adalah metode yang ditentukan oleh NSApplication .

Luncurkan aplikasi dan Anda akan melihat menu dengan mengklik ikon aplikasi.



Coba klik pada menu - memilih Print Quote akan menampilkan penawaran di konsol Xcode, dan Quit Quotes mengakhiri aplikasi.

Tambahkan sembulan


Anda melihat betapa mudahnya menambahkan menu dari kode, tetapi menampilkan kutipan di konsol Xcode jelas bukan yang diharapkan pengguna dari aplikasi. Sekarang kita akan menambahkan controller tampilan sederhana untuk menampilkan kutipan dengan cara yang benar.

Buka menu File / New / File ... , pilih template macOS / Source / Cocoa Class dan klik Next .



  • beri nama kelas QuotesViewController
  • membuat pewaris NSViewController
  • pastikan kotak centang Juga buat file XIB untuk antarmuka pengguna tidak dicentang
  • atur bahasa ke Swift

Terakhir, klik Next lagi, pilih lokasi untuk menyimpan file, dan klik Buat .
Sekarang buka Main.storyboard . Rentangkan View Controller Scene dan pilih View Controller instance .



Pertama-tama pilih Inspektur Identitas dan ubah kelas menjadi QuotesViewController , lalu atur Storyboard ID ke QuotesViewController

Sekarang tambahkan kode berikut ke akhir file QuotesViewController.swift :

 extension QuotesViewController { // MARK: Storyboard instantiation static func freshController() -> QuotesViewController { //1. let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil) //2. let identifier = NSStoryboard.SceneIdentifier("QuotesViewController") //3. guard let viewcontroller = storyboard.instantiateController(withIdentifier: identifier) as? QuotesViewController else { fatalError("Why cant i find QuotesViewController? - Check Main.storyboard") } return viewcontroller } } 

Apa yang terjadi di sini:

  1. kami mendapatkan tautan ke Main.storyboard .
  2. buat pengidentifikasi adegan yang cocok dengan yang baru saja kita instal di atas.
  3. buat instance dari QuotesViewController dan kembalikan.

Anda membuat metode ini, jadi sekarang semua orang yang menggunakan QuotesViewController tidak perlu tahu bagaimana itu dibuat. Itu hanya bekerja.

Perhatikan fatalError di dalam pernyataan penjaga . Bisa menyenangkan untuk menggunakannya atau pernyataan Kegagalan sehingga jika ada sesuatu yang salah dalam pengembangan, Anda sendiri, dan anggota tim pengembangan lainnya, mengetahui.

Sekarang kembali ke AppDelegate.swift . Tambahkan properti baru.

 let popover = NSPopover() 

Kemudian ganti pplicationDidFinishLaunching (_ :) dengan kode berikut:

 func applicationDidFinishLaunching(_ aNotification: Notification) { if let button = statusItem.button { button.image = NSImage(named:NSImage.Name("StatusBarButtonImage")) button.action = #selector(togglePopover(_:)) } popover.contentViewController = QuotesViewController.freshController() } 

Anda telah mengubah aksi klik untuk memanggil metode togglePopover (_ :) , yang akan kami tulis sedikit kemudian. Selain itu, alih-alih mengonfigurasi dan menambahkan menu, kami mengonfigurasi jendela sembulan yang akan menampilkan sesuatu dari QuotesViewController .

Tambahkan tiga metode berikut ke 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 () menunjukkan munculan. Anda cukup menunjukkan dari mana asalnya, macOS memposisikannya dan menggambar panah, seolah-olah itu muncul dari menu bar.

closePopover () baru saja menutup popup, dan togglePopover () adalah metode yang menunjukkan atau menyembunyikan popup, tergantung pada kondisinya .

Luncurkan aplikasi dan klik ikonnya.



Semuanya baik-baik saja, tetapi di mana isinya?

Kami menerapkan Pengendali Tampilan Penawaran


Pertama, Anda membutuhkan model untuk menyimpan kutipan dan atribut. Buka menu File / New / File ... dan pilih template macOS / Source / Swift File , lalu Next . Beri nama file Penawaran dan klik Buat .

Buka file Quote.swift dan tambahkan kode berikut ke dalamnya:

 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)" } } 

Di sini kita mendefinisikan struktur kutipan sederhana dan properti statis yang mengembalikan semua kutipan. Karena kami membuat kutipan yang sesuai dengan protokol CustomStringConvertible , kami dapat dengan mudah mendapatkan teks yang diformat dengan mudah.

Ada kemajuan, tetapi kami masih membutuhkan kontrol untuk menampilkan semua ini.

Tambahkan elemen antarmuka


Buka Main.storyboard dan tarik keluar 3 tombol ( Push Button ) dan label ( Multiline Label) pada pengontrol tampilan.

Posisikan tombol dan label sehingga terlihat seperti ini:



Pasang tombol kiri ke tepi kiri dengan celah 20 dan tengah secara vertikal.
Pasang tombol kanan ke tepi kanan dengan celah 20 dan tengah secara vertikal.
Pasang tombol bawah ke tepi bawah dengan celah 20 dan tengah secara horizontal.
Pasang tepi kiri dan kanan tanda ke tombol dengan celah 20, di tengah secara vertikal.



Anda akan melihat beberapa kesalahan tata letak, karena tidak ada informasi yang cukup untuk tata letak otomatis untuk mengetahuinya.

Tetapkan Prioritas Memeluk Konten Horizontal ke 249 untuk memungkinkan label untuk mengubah ukuran.



Sekarang lakukan hal berikut:

  • atur gambar tombol kiri ke NSGoLeftTemplate dan hapus judulnya
  • atur gambar tombol kanan ke NSGoRightTemplate dan hapus judulnya
  • atur judul tombol di bawah ini ke Quit Quotes .
  • atur perataan teks label ke tengah.
  • pastikan Line Break pada label diatur ke Word Wrap .


Sekarang buka QuotesViewController.swift dan tambahkan kode berikut untuk implementasi kelas QuotesViewController :

 @IBOutlet var textLabel: NSTextField! 


Tambahkan ekstensi ini ke implementasi kelas. Sekarang di QuotesViewController.swift ada dua ekstensi kelas.

 // MARK: Actions extension QuotesViewController { @IBAction func previous(_ sender: NSButton) { } @IBAction func next(_ sender: NSButton) { } @IBAction func quit(_ sender: NSButton) { } } 

Kami baru saja menambahkan outlet untuk label yang akan kami gunakan untuk menampilkan kutipan, dan 3 metode rintisan yang akan kami sambungkan dengan tombol.

Menghubungkan kode dengan Interface Builder


Catatan: Xcode telah menempatkan lingkaran di sebelah kiri kode Anda - di sebelah kata kunci IBAction dan IBOutlet .



Kami akan menggunakannya untuk menghubungkan kode ke UI.

Sambil menahan tombol alt , klik Main.storyboard di navigator proyek . Dengan demikian, storyboard terbuka di Asisten Editor di sebelah kanan, dan kode di sebelah kiri.

Seret lingkaran ke kiri textLabel ke label pada pembuat antarmuka . Dengan cara yang sama, gabungkan metode sebelumnya , berikutnya, dan keluar dengan tombol kiri, kanan, dan bawah.



Luncurkan aplikasi Anda.



Kami menggunakan ukuran sembulan default. Jika Anda ingin popup yang lebih besar atau lebih kecil, cukup ubah ukurannya di storyboard .

Menulis kode untuk tombol


Jika Anda belum menyembunyikan Asisten Editor , klik Cmd-Return atau Vew> Editor Standar> Tampilkan Editor Standar

Buka QuotesViewController.swift dan tambahkan properti berikut ke implementasi kelas:

 let quotes = Quote.all var currentQuoteIndex: Int = 0 { didSet { updateQuote() } } 

Properti kutipan berisi semua kutipan, dan currentQuoteIndex adalah indeks kutipan yang saat ini sedang ditampilkan. CurrentQuoteIndex juga memiliki pengamat properti untuk memperbarui konten label dengan kutipan baru ketika indeks berubah.

Sekarang tambahkan metode berikut:

 override func viewDidLoad() { super.viewDidLoad() currentQuoteIndex = 0 } func updateQuote() { textLabel.stringValue = String(describing: quotes[currentQuoteIndex]) } 

Saat tampilan dimuat, kami menetapkan indeks kutipan ke 0, yang pada gilirannya mengarah ke pembaruan ke antarmuka. updateQuote () cukup memperbarui label teks untuk menampilkan penawaran. sesuai sekarangQuoteIndex .

Akhirnya, perbarui metode ini dengan kode berikut:

 @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) } 

Metode berikutnya () dan sebelumnya () menggilir semua kutipan. berhenti menutup aplikasi.

Luncurkan aplikasi:



Pemantauan acara


Ada satu hal lagi yang diharapkan pengguna dari aplikasi kita - sembunyikan jendela sembul ketika pengguna mengklik di suatu tempat di luarnya. Untuk melakukan ini, kita memerlukan mekanisme yang disebut macOS global event monitor .

Buat file Swift baru, sebut saja EventMonitor , dan ganti kontennya dengan kode berikut:

 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 } } } 

Saat menginisialisasi turunan dari kelas ini, kami memberikannya masker acara yang akan kami dengarkan (seperti penekanan tombol, gulungan roda mouse, dll.) Dan pengendali acara.
Ketika kita siap untuk mulai mendengarkan, mulai () panggil addGlobalMonitorForEventsMatchingMask (_: handler :) , yang mengembalikan objek yang kita simpan. Segera setelah peristiwa yang terkandung dalam topeng terjadi, sistem memanggil penangan Anda.

Untuk berhenti memantau acara, removeMonitor () disebut in stop () dan kami menghapus objek dengan menyetelnya ke nil.

Yang tersisa bagi kami adalah memanggil start () dan stop () pada waktu yang tepat. Kelas juga memanggil stop () pada deinitializer untuk membersihkan.

Menghubungkan Monitor Acara


Buka AppDelegate.swift untuk terakhir kalinya dan tambahkan properti baru:

 var eventMonitor: EventMonitor? 

Kemudian tambahkan kode ini untuk mengonfigurasi acara monitor di akhir applicationDidFinishLaunching (_ :)

 eventMonitor = EventMonitor(mask: [.leftMouseDown, .rightMouseDown]) { [weak self] event in if let strongSelf = self, strongSelf.popover.isShown { strongSelf.closePopover(sender: event) } } 

Ini akan menginformasikan aplikasi Anda ketika Anda mengklik tombol kiri atau kanan. Harap dicatat: pawang tidak akan dipanggil untuk menanggapi klik mouse di dalam aplikasi Anda. Inilah sebabnya mengapa popup tidak akan ditutup saat Anda mengklik di dalamnya.

Kami menggunakan referensi yang lemah untuk diri sendiri untuk menghindari bahaya dari siklus tautan yang kuat antara AppDelegate dan EventMonitor .

Tambahkan kode berikut di akhir metode showPopover (_ :) :

 eventMonitor?.start() 

Di sini kita mulai memantau acara ketika jendela sembul muncul.

Sekarang tambahkan kode di akhir metode closePopover (_ :) :

 eventMonitor?.stop() 

Di sini kita mengakhiri pemantauan ketika popup ditutup.

Aplikasi sudah siap!

Kesimpulan


Di sini Anda akan menemukan kode lengkap untuk proyek ini.

Anda telah belajar cara mengatur menu dan pop-up di aplikasi yang terletak di bilah menu. Mengapa tidak bereksperimen dengan banyak tag atau teks berformat untuk mendapatkan tanda kutip yang lebih baik? Atau hubungkan backend untuk menerima penawaran dari Internet? Atau Anda ingin menggunakan keyboard untuk bernavigasi di antara kutipan?

Tempat yang baik untuk penelitian adalah dokumentasi resmi: NSMenu , NSPopover dan NSStatusItem .

Source: https://habr.com/ru/post/id447754/


All Articles