クリアメモリ

プログラミングやモーショングラフィックス、便利なアプリケーションなど雑多に記録するブログ

【Swift】メニューバー常駐アプリを作るNSMenuの使い方

f:id:clrmemory:20170422182119p:plain 

Swift3でMacOSX開発(cocoa programming)について備忘録を兼ねて記載します。

 

今回紹介する内容は、メニューバー(ステータスバー)に常駐するタイプのアプリケーションの作り方についてです。

 

 

はじめに

 

今回紹介するアプリケーションは、Xcodeを使用して作成します。

 

まずは、サンプルプロジェクトを作成してしまいましょう。

 

 

「Create a new Xcode project」を選択。

 

 

上部のタブを「macOS」に切り替えた状態で、「Cocoa」を選択してNextで進みましょう。

 

 

プロジェクトの名前などの項目を指定するウィンドウが開くので、今回は「Sample」と言う名前にしました。

Use Storyboardsにチェックを入れて、ストーリーボードも追加しておきましょう。

 

確認したらNext。

 

 

これでプロジェクトの作成が完了しました。

デフォルトのストーリーボードはこんな感じになっています。

 

起動時の設定

 

今回開発するアプリケーションでは、メニューバーに常駐させるのでウィンドウを表示させる必要はありませんよね。

 

そこで、アプリを起動した時に自動で表示されるウィンドウの設定を行いましょう。

 

 

storyboardを開くと、以下の画像のようにWindow Controllerに矢印が向いていると思います。

 

 

この矢印が起動時に表示されるウィンドウを示しているので、以下を参考にしてチェックを外しましょう。

 

 

矢印がついているWindowControllerを選択した状態で、「Show the Attributes inspector」を選択します。

 

 

このような項目が表示されるので「Is Initial Controller」のチェックを外します。

 

 

Window Controllerを確認すると、先ほどの矢印が消えていますね。

 

 

これで不要なウィンドウは表示されなくなりました。

 

info.plistの編集

 

続いて「info.plist」を編集していきます。

「Show the Project navigator」からinfo.plistを選択してください。

 

 

以下のような項目が表示されると思います。

 

 

 

この中のどれでも良いので、マウスカーソルを合わせてみてください。

Listの名前の右側に「+」「-」が表示されるので「+」を選択しましょう。

 

 

新しい項目が追加されるので「Application is agent(UIElement)」を選択。

※Application is agent(UIElement)の追加による変化については後述。

 

 

 

 

追加された「Application is agent(UIElement)」の右の項目を、順に「Boolern」「YES」というように変更してください。

 

 

これでinfo.plistの編集は完了しました。

 

さて、ここで追加したApplication is agent(UIElement)の役割について説明します。

先ほどBoolernの値をYESに変更したと思いますが、このようにすることで「Dockにアプリケーションを表示しない」ようにできます。

 

メニューバーに常駐するアプリであれば、Dockにアプリを表示する必要がないので、このように編集しました。

 

 

Dockというのは、デフォルトであれば画面の下部にある、アプリケーションが表示される箇所のことです。

 

メニューを設置

 

続いて、メニューバーに表示されたアプリケーションをクリックした時に表示されるメニューを作成しましょう。

storyboardを開いてください。

 

 

右下部のオブジェクト欄から「Menu」を探して追加します。

検索欄を使用すると見つけやすいですね。

 

!ここで注意点なのですが、Menuを設置する場所はどこでも良いというわけではありません。

 

 

以下の画像にもあるように「Main Menu」を選択します。

 

 

この状態で、先ほどのMenuを青いキューブの隣に配置してください。

 

 

このようになっていればオッケーです。

 

では、続いてメニューの内容を編集しましょう。

今回はサンプルなので、適当なアイテムにしてみました。

 

 

アプリケーションの終了ボタンについては、必ず必要になるので後でコードから追加しましょう。

 

 

 

せっかくなので、メニューをクリックしたらウィンドウを表示するようにしてみました。

メニューバーに常駐するアプリケーションを開発するという内容とは少し離れているので、この項目は飛ばしても構いません。

 

では、ここからは「Swift3」を使ってメニューバーに常駐するアプリケーションにしていきましょう。

 

Swift3で実装する

 

今回編集するのは「AppDelegate.swift」です。

ViewController.swiftではないので注意してください。

 

 

初期状態では、このようになっていると思います。

まずは完成形を確認してください。そのあとで解説に入ります。

 

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    //メニューとOutlet接続。
    @IBOutlet weak var menu: NSMenu!

    //メニューバーに表示されるアプリケーションを作成
    let statusItem = NSStatusBar.system().statusItem(withLength: NSVariableStatusItemLength)


    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Insert code here to initialize your application


        // メニューバーに表示されるアプリ。今回は文字列で設定
        self.statusItem.title = "Sample"
        //メニューのハイライトモードの設定
        self.statusItem.highlightMode = true
        //メニューの指定
        self.statusItem.menu = menu


        //アプリ終了ボタンを作成
        let quitItem = NSMenuItem()
        //終了ボタンのテキスト
        quitItem.title = "Quit Application"
        //終了ボタンをクリックした時の動作。 ˚˚˚ func quitを呼び出す。˚˚˚
        quitItem.action = #selector(AppDelegate.quit(_:))
        //作成したボタンを追加。
        menu.addItem(quitItem)

    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }

    func quit(_ sender: Any){
        //アプリケーションの終了
        NSApplication.shared().terminate(self)
    }
}

 

このようなコードを書くことでメニューバーに常駐するMacOSXアプリを作成できました。

 

 

コードの解説

 

では解説していきます。

 

menu

 

まずはじめに「menu」がありますが、こちらは先ほどstoryboardで作成した「Menu」とOutlet接続してあります。

 

 

Outlet接続は、メニューを選択した状態で「右クリック」しながらAppDelegateに追加でできるあれですね。

もしくはCtrl + ドラッグでもできたと思います。

 

statusItem

 

続いて、メニューバーに表示されるアプリケーションの作成です。

このあとに出てくるstatusItem.titleのように指定することで、タイトル名や動作などを設定できます。

 

 

withlength: NSVariableStatusItemLengthとすることで、テキストを省略することなく表示できるようになります。

 

※このように設定するとエラーが出る可能性があるようです。

そのような時は「NSVariableStatusItemLength」ではなく「-2」にすると回避できるみたいです。

 

例:let statusItem = NSStatusBar.system( ).StatusItem(withLength: -2)

 

self.statusItem.title

 

コードを見ればわかる通り、メニューバーに表示されるアイコンのタイトルを指定しています。

 

self.statusItem.image = “xxx.png”

のように指定するとアイコンをテキストではなく画像にすることができたと思います。(未確認)

 

statusItem.highlightMode

 

このコードでは、メニューバーのアイコンをクリックした時にハイライトさせるかの設定になります。

以下を確認してください。

 

highlightMode = true

 

highlightMode = false

 

わかりづらいですが2種を比較すると、「Sample」が光るかどうかという違いがありますね。

おそらくですが、この違いしかないのではないかと思います。私はこれ以外に確認できませんでした。

 

self.statusItem.menu

 

ここで先ほどOutlet接続したmenuが登場します。

このようにすることで、statusItemのメニューにstoryboardで作成したメニューを割り当てることができました。

 

quitItem

 

storyboardからの作成をあえて避けていた「終了ボタン」を追加します。

NSMenuItem( ) でquitItemにメニューアイテムを与えます。

 

quitItem.title

 

アイコンの時と同じように、終了ボタンのタイトルも指定できます。

 

あまり長すぎると不恰好になってしまう可能性があるので注意しましょう。

 

 

…。

 

quitItem.action

 

終了ボタンをクリックした時に呼ばれるコードを指定しましょう。

 

ここでは、後で出てくる「quit」を呼び出しています。

(      #selector(AppDelegate.quite( _: ) )   )

 

ついでにここで「quit」も説明してしまいましょう。

NSApplication.shared( ).terminate( ) でアプリケーションを終了するという命令を出すことができます。

 

 

このterminateにselfを与えてあげることで、このアプリを終了させるという内容になります。

 

menu.addItem(quitItem)

 

最後に作成したquitItemをメニューの中に追加します。

Outlet接続したmenuの中に追加している点に注意してください。

 

 

これでコードの解説を終了します。

まとめ

 

今回紹介した方法を使うことで、メニューバーに常駐するアプリケーションを開発できるようになりました。

 

似たような方法で、CustomViewをポップアップさせることもできるので、そのうち紹介したいと思います。

記事をアップしたら随時追記していく予定です。

 

 

メニューバーに常駐しているアプリのほとんどに「Preferences…」のような項目があると思うので、ぜひ参考にしてみてください。

 

ではまた。

 

新着記事