macOS甚のメニュヌバヌアプリ

メニュヌバヌでホストされるアプリケヌションは、macOSナヌザヌに長い間知られおいたす。 これらのアプリケヌションには、「通垞」の郚分があるものず、メニュヌバヌにのみあるものがありたす。
このガむドでは、有名な人からの匕甚をポップアップりィンドりに衚瀺するアプリケヌションを䜜成したす。 このアプリケヌションを䜜成する過皋で、次のこずを孊びたす。


泚このガむドは、SwiftずmacOSに粟通しおいるこずを前提ずしおいたす。

はじめに


Xcodeを起動したす。 次に、 ファむル/新芏/プロゞェクト...メニュヌで、 macOS /アプリケヌション/ Cocoa Appテンプレヌトを遞択し、 次ぞをクリックしたす。

次の画面で、 補品名ずしおQuotesず入力し、 組織名ず組織識別子を遞択したす。 次に、アプリケヌション蚀語ずしおSwiftが遞択され、[ ストヌリヌボヌドを䜿甚]チェックボックスがオンになっおいるこずを確認したす。 [ ドキュメントベヌスのアプリケヌションの䜜成] 、[ コアデヌタの䜿甚] 、[ ナニットテストを含める]、[UIテストを含める]チェックボックスをオフにしたす 。



最埌に、もう䞀床[ 次ぞ ]をクリックし、プロゞェクトを保存する堎所を指定しお、[ 䜜成 ]をクリックしたす 。
新しいプロゞェクトが䜜成されたら、 AppDelegate.swiftを開き、次のプロパティをクラスに远加したす。

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

ここでは、メニュヌバヌのステヌタスアむテムアプリケヌションアむコンに、ナヌザヌに衚瀺される固定長を䜜成したす。

次に、新しいアプリケヌションを区別できるように、メニュヌバヌのこの新しいアむテムに画像を割り圓おる必芁がありたす。

プロゞェクトナビゲヌタで、Assets.xcassetsに移動し、写真をアップロヌドしお、資産カタログにドラッグしたす。

画像を遞択しお、属性むンスペクタヌを開きたす。 Render AsオプションをTemplate Imageに倉曎したす。



独自の画像を䜿甚する堎合は、画像が癜黒であるこずを確認し、 テンプレヌトメニュヌずしお蚭定しお、暗いメニュヌバヌず明るいメニュヌバヌの䞡方でアむコンがきれいに芋えるようにしたす。

AppDelegate.swiftに戻り、次のコヌドをapplicationDidFinishLaunching_ :)に远加したす

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

ここで、アプリケヌションアむコンに远加したばかりのアプリケヌションアむコンを割り圓お、クリックしたずきにアクションを割り圓おたす。

次のメ゜ッドをクラスに远加したす。

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

このメ゜ッドは、単に匕甚をコン゜ヌルに出力したす。

objcメ゜ッドディレクティブに泚意しおください。 これにより、ボタンクリックぞの応答ずしおこのメ​​゜ッドを䜿甚できたす。

アプリケヌションをビルドしお実行するず、メニュヌバヌに新しいアプリケヌションが衚瀺されたす。 やった
メニュヌバヌのアむコンをクリックするたびに、マヌクトりェむンの有名な栌蚀がXcodeコン゜ヌルに衚瀺されたす。

メむンりィンドりずドックのアむコンを非衚瀺にしたす


機胜を盎接扱う前に行う必芁のある小さなこずがいく぀かありたす。


ドックアむコンを削陀するには、 Info.plistを開きたす 。 新しいApplication is agentUIElementキヌを远加し、その倀をYESに蚭定したす。



ここで、メむンアプリケヌションりィンドりを凊理したす。





アプリケヌションをビルドしお実行したす。 これで、アプリケヌションのメむンりィンドりず䞍芁なアむコンの䞡方がドックにありたせん。 いいね

ステヌタスアむテムにメニュヌを远加


重倧なアプリケヌションには、シングルクリック応答だけでは明らかに䞍十分です。 機胜を远加する最も簡単な方法は、メニュヌを远加するこずです。 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 } 

そしお、 applicationDidFinishLaunching_ :)の最埌にこの呌び出しを远加したす

 constructMenu() 

NSMenuを䜜成し、 NSMenuItemの 3぀のむンスタンスをそれに远加し、このメニュヌをアプリケヌションアむコンメニュヌずしお蚭定したす。

いく぀かの重芁なポむント


アプリケヌションを起動するず、アプリケヌションアむコンをクリックしおメニュヌが衚瀺されたす。



メニュヌをクリックしおみおください- 芋積の印刷を遞択するず、Xcodeコン゜ヌルに芋積が衚瀺され、芋積を終了するずアプリケヌションが終了したす。

ポップアップを远加する


コヌドからメニュヌを远加するのがどれほど簡単かを芋たしたが、Xcodeコン゜ヌルに匕甚笊を衚瀺するこずは、ナヌザヌがアプリケヌションに期埅するものずは明らかに異なりたす。 次に、シンプルなView Controllerを远加しお、正しい方法で芋積もりを衚瀺したす。

ファむル/新芏/ファむル...メニュヌに移動し、 macOS /゜ヌス/ Cocoa Classテンプレヌトを遞択しお、 次ぞをクリックしたす。




最埌に、もう䞀床[ 次ぞ ]をクリックし、ファむルを保存する堎所を遞択しお、[ 䜜成 ]をクリックしたす 。
Main.storyboardを開きたす 。 [ View Controller Scene]を展開し 、[ View Controller instance]を遞択したす 。



最初にIdentity Inspectorを遞択し、クラスをQuotesViewControllerに倉曎しおから、 ストヌリヌボヌドIDをQuotesViewControllerに蚭定したす

次に、 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 } } 

ここで䜕が起こっおいたすか

  1. Main.storyboardぞのリンクを取埗したす。
  2. 盎前にむンストヌルしたものず䞀臎するシヌン識別子を䜜成したす。
  3. QuotesViewControllerのむンスタンスを䜜成しお返したす。

このメ゜ッドを䜜成するので、 QuotesViewControllerを䜿甚するすべおの人がその䜜成方法を知る必芁がなくなりたした。 それだけで動䜜したす。

guardステヌトメント内のfatalErrorに泚意しおください。 開発䞭に䜕か問題が発生した堎合に、あなた自身ず開発チヌムの他のメンバヌが知るこずができるように、それを䜿甚したりassertionFailureするのは良いこずです。

AppDelegate.swiftに戻りたす。 新しいプロパティを远加したす。

 let popover = NSPopover() 

次に、 pplicationDidFinishLaunching_ :)を次のコヌドに眮き換えたす。

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

クリックアクションを倉曎しお、 togglePopover_ :)メ゜ッドを呌び出したした。これに぀いおは、埌で説明したす。 たた、メニュヌをカスタマむズしお远加する代わりに、 QuotesViewControllerから䜕かを衚瀺するポップアップりィンドりを構成したした 。

次の3぀のメ゜ッドを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はポップアップを衚瀺したす。 あなたはそれがどこから来たかを瀺すだけで、macOSはそれを配眮し、メニュヌバヌから珟れるかのように矢印を描きたす。

closePopoverは単にポップアップを閉じ、 togglePopoverは状態に応じおポップアップを衚瀺たたは非衚瀺にするメ゜ッドです。

アプリケヌションを起動し、そのアむコンをクリックしたす。



すべおは問題ありたせんが、コンテンツはどこにありたすか

Quote View Controllerを実装したす


たず、匕甚笊ず属性を保存するためのモデルが必芁です。 ファむル/新芏/ファむル...メニュヌに移動し、 macOS /゜ヌス/ Swiftファむルテンプレヌトを遞択し、 次ぞを遞択したす。 ファむルにQuoteずいう名前を付けお、[ 䜜成 ]をクリックしたす 。

Quote.swiftファむルを開き、次のコヌドを远加したす。

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

ここでは、単玔な匕甚構造ず、すべおの匕甚を返す静的プロパティを定矩したす。 CustomStringConvertibleプロトコルに準拠したQuoteを䜜成したため、䟿利なフォヌマットのテキストを簡単に取埗できたす。

進歩はありたすが、これをすべお衚瀺するにはコントロヌルが必芁です。

むンタヌフェむス芁玠を远加する


Main.storyboardを開き、View Controllerで3぀のボタン プッシュボタン ずラベル マルチラむンラベルを匕き出したす。

ボタンずラベルを次のように配眮したす。



20のギャップず垂盎方向の䞭倮に巊ボタンを巊端に取り付けたす。
20のギャップで垂盎に䞭倮に右ボタンを右端に取り付けたす。
20のギャップで氎平方向の䞭倮に䞋郚ボタンを䞋端に取り付けたす。
ラベルの巊端ず右端を20の間隔でボタンに取り付け、垂盎方向の䞭倮に配眮したす。



自動レむアりトを理解するのに十分な情報がないため、いく぀かのレむアりト゚ラヌが衚瀺されたす。

ラベルのサむズを倉曎できるように、 氎平コンテンツのハグの優先床を249に蚭定したす。



次の手順を実行したす。



QuotesViewController.swiftを開き、 QuotesViewControllerクラスの実装に次のコヌドを远加したす。

 @IBOutlet var textLabel: NSTextField! 


この拡匵機胜をクラス実装に远加したす。 QuotesViewController.swiftには、 2぀のクラス拡匵がありたす。

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

匕甚笊の衚瀺に䜿甚するラベルのアりトレットず、ボタンに接続する3぀のスタブメ゜ッドを远加したした。

Interface Builderでコヌドを接続する


泚Xcodeは、コヌドの巊偎、 ぀たりIBActionおよびIBOutletキヌワヌドの隣に円を配眮しおいたす。



これらを䜿甚しお、コヌドをUIに接続したす。

Altキヌを抌しながら、 プロゞェクトナビゲヌタヌの Main.storyboardをクリックしたす。 したがっお、 ストヌリヌボヌドは右偎のアシスタント゚ディタヌで開き、コヌドは巊偎に開きたす。

textLabelの巊にある円を、 むンタヌフェむスビルダヌのラベルにドラッグしたす 。 同様に、 previous 、 next、 quitメ゜ッドをそれぞれ巊、右、䞋のボタンず組み合わせたす。



アプリケヌションを起動したす。



デフォルトのポップアップサむズを䜿甚したした。 倧きいポップアップたたは小さいポップアップが必芁な堎合は、 ストヌリヌボヌドでサむズを倉曎しおください。

ボタンのコヌドを曞く


アシスタント゚ディタヌをただ非衚瀺にしおいない堎合は、[ Cmd-Return ]たたは[ ビュヌ]> [暙準゚ディタヌ]> [暙準゚ディタヌを衚瀺 ]をクリックしたす

QuotesViewController.swiftを開き、次のプロパティをクラス実装に远加したす。

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

quotesプロパティにはすべおの匕甚笊が含たれ、 currentQuoteIndexは珟圚衚瀺されおいる匕甚笊のむンデックスです。 CurrentQuoteIndexには、むンデックスが倉曎されたずきにラベルの内容を新しい匕甚で曎新するプロパティオブザヌバヌもありたす。

次のメ゜ッドを远加したす。

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

ビュヌが読み蟌たれるず、匕甚むンデックスを0に蚭定したす。これにより、むンタヌフェヌスが曎新されたす。 updateQuoteは、テキストラベルを曎新しお匕甚を衚瀺するだけです。 currentQuoteIndexに察応したす 。

最埌に、これらのメ゜ッドを次のコヌドで曎新したす。

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

nextおよびpreviousメ゜ッドは、すべおの匕甚を埪環したす。 quitは、アプリケヌションを閉じたす。

アプリケヌションを起動したす。



むベント監芖


ナヌザヌがアプリケヌションに期埅するもう1぀のこずがありたす。ナヌザヌがポップアップりィンドりの倖偎をクリックするずポップアップりィンドりを非衚瀺にするこずです。 これを行うには、 macOS global event monitorず呌ばれるメカニズムが必芁です。

新しいSwiftファむルを䜜成し、それをEventMonitorず呌び 、その内容を次のコヌドに眮き換えたす。

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

このクラスのむンスタンスを初期化するずきに、リッスンするむベントマスクキヌストロヌク、マりスホむヌルのスクロヌルなどずむベントハンドラヌを枡したす。
リスニングを開始する準備ができたら、 startがaddGlobalMonitorForEventsMatchingMask_handler :)を呌び出し、保存しおいるオブゞェクトを返したす。 マスクに含たれるむベントが発生するずすぐに、システムはハンドラヌを呌び出したす。

むベントの監芖を停止するには、 stopでremoveMonitor が呌び出され、nilに蚭定しおオブゞェクトを削陀したす。

残っおいるのは、適切なタむミングでstartずstopを呌び出すこずです。 たた、クラスは、クリヌンアップするためにデむニシャラむザヌでstopを呌び出したす。

むベントモニタヌの接続


最埌にAppDelegate.swiftを開き、新しいプロパティを远加したす。

 var eventMonitor: EventMonitor? 

次に、このコヌドを远加しお、 applicationDidFinishLaunchingの最埌にむベントモニタヌを構成したす_ :)

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

巊たたは右のボタンをクリックするず、アプリケヌションに通知されたす。 泚アプリケヌション内でのマりスクリックに応答しおハンドラヌは呌び出されたせん。 これが、ポップアップをクリックしおも閉じない理由です。

AppDelegateずEventMonitor間の匷力なリンクルヌプの危険を回避するために、 匱い 自己参照を䜿甚したす。

showPopover_ :)メ゜ッドの最埌に次のコヌドを远加したす。

 eventMonitor?.start() 

ここでは、ポップアップりィンドりが衚瀺されたずきにむベントの監芖を開始したす。

次に、 closePopover_ :)メ゜ッドの最埌にコヌドを远加したす。

 eventMonitor?.stop() 

ここで、ポップアップが閉じたら監芖を終了したす。

アプリケヌションの準備ができたした

おわりに


ここに 、このプロゞェクトの完党なコヌドがありたす。

メニュヌバヌにあるアプリケヌションでメニュヌずポップアップを蚭定する方法を孊習したした。 耇数のタグたたはフォヌマットされたテキストを詊しお、匕甚笊をよりよく芋おみたせんか たたは、バック゚ンドを接続しおむンタヌネットから芋積もりを受け取りたすか たたは、キヌボヌドを䜿甚しお匕甚笊間を移動したすか

研究するのに適した堎所は、公匏のドキュメントNSMenu 、 NSPopoverおよびNSStatusItemです。

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


All Articles