macos
Swift

会社の行動規範浸透を図るため、メニューバーからいつでも確認できるアプリを作った🤔

これはRetty Inc. AdventCalendar2018 17日目の記事です。
昨日は @shyneさんの 既存AndroidアプリにFlutterを導入して12.5倍速で開発する でした。

はじめに

皆さんはmacOSのメニューバーに常駐するアプリをどれくらい使われているでしょうか?
よく使われているアプリケーションはDropbox, JetBrains ToolBox, macOS設定系, ...etc と様々なアプリを使われているのではないでしょうか?

これらのメニューバーアプリの良さは気軽にアクセスできることだと思います。
この利点を利用して、日頃忘れがち(?)な会社の行動規範(RettyWay)をいつでも確認できるようなアプリを作って行きたいと思います。

脱線しますが、RunCat:cat2:というCPU利用率を表示するアプリがとてもユニークで良かったので愛用しています。(オススメです:fire:

完成したもの

完成物は簡単な作りで画像を1枚表示しているだけです。1
このアプリを常駐させておけば、小休憩時に行動規範を確認しやすくなりますね!? :family: ?2

RettyWayとは?

Rettyの行動規範を指します。
詳細はこちらの記事: Rettyらしさは努力で守る。組織拡大のフェーズで行動規範「Retty Way」が果たす役割を見て頂けると幸いです。 :pray:

上記の記事でも取り上げられているのですが、RettyWayは組織の状況に合わせて変化しています。
そのため、単純な失念や認識の齟齬をなくす目的としても、今回のアプリは有用と言えるかもしれません。

メニューバーアプリ(Menu Bar Extras) とは?

この記事内でメニューバーアプリと言っているモノはHuman Interface Guidelines内のMenu Bar Extrasを指しています。

Display a menu—not a popover—when the user clicks your menu bar extra. Unless the app functionality you want to expose is too complex for a menu, you should avoid presenting it in a popover. See Popovers.

また上記ガイドラインに目を通すと、今回作るようなポップアップを表示するアプリは、ガイドライン的に適切でないようです。

前置きが長くなりましたが、早速作っていきましょう。

実装

常駐型アプリを作る上で必要な作業は下記の3点です。
- メニューバーのアイコン表示・指定
- Dockアイコンの無効化
- Main Windowの非表示化(削除)

下の2つは好みですが、メニューバー機能のみのアプリであれば実装することが好ましいでしょう。

プロジェクトの作成

Xcodeを立ち上げ、設定の File → New → Project → macOS → Cocoa Appでプロジェクトを用意します。

File/New/Project の選択 macOS/Cocoa Appの選択
image.png image.png

メニューバー用のTemplateが用意されている訳ではない為、 Cocoa Appから作ることになります。

メニューバーのアイコンを指定する

アイコン用のAssetsを用意

適当な画像をAssetsに追加し、Rendar AsTemplate Imageを指定。
icon

メニューバーにアイコンを表示

  1. メニューアイコンとアイコンから表示するPopoverを用意
  2. ボタンにアイコン画像とアクションを指定
  3. Popoverで表示するViewを指定
    • また③を行うにあたって、StoryboardのidentityにViewControllerを指定してください。
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    // ①
    private let menuBarItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
    private let popover = NSPopover()

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // ②
        if let button = self.menuBarItem.button {
            button.image = NSImage(named: NSImage.Name("MenuBarIcon"))
            button.action = #selector(togglePopover(_:))
        }
        // ③
        self.popover.contentViewController = ViewController.freshController()
    }
}

// ②
extension AppDelegate {
    @objc func togglePopover(_ sender: Any) {
        if self.popover.isShown {
            self.closePopover(sender)
        } else {
            self.showPopover(sender)
        }
    }

    private func showPopover(_ sender: Any) {
        guard let button = self.menuBarItem.button else { return }
        self.popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
    }    

    private func closePopover(_ sender: Any) {
        self.popover.performClose(sender)
    }
} 

// ③
extension ViewController {
    static func freshController() -> ViewController {
        let storyboard = NSStoryboard(name: "Main", bundle: nil)
        let identifier: NSStoryboard.SceneIdentifier = "ViewController"
        guard let viewController = storyboard.instantiateController(withIdentifier: identifier) as? ViewController else { fatalError("...") }
        return viewController
    }
}

ここまでで、こんな感じのものが出来たでしょうか?(適当にNSImageViewを配置して表示しています3
image.png
この段階ではアプリのMain Windowが表示されてしまっていますが、不要なので消してしまいましょう。

Main Windowの非表示化(削除)

といっても、StoryBoard上からWindow Controller Sceneを消すだけでOKです。
Main Window

Dockアイコンの無効化

現状では下記画像のようにアプリのアイコンがアクティブな状態で表示されています。
メニューバー常駐型のアプリでは、Dockにアイコンが表示されている必要性が低い & またそういうアプリが多い為4、消してしまいましょう。
image.png

image.png
info.plistを開き、上記プロパティを追加することで、Dock Iconを消せます。
XMLのKeyはLSUIElementです。


:fire:以上でメニューバーに常駐するアプリが完成したかと思います:fire:
表示させるコンテンツは皆さん良しなに実装してみてください。
一応完成サンプル : GitHubを置いておきます。

最後に

いかがでしたでしょうか?

今回は個人的に作りたかっただけで、半ば無理やりRettyを紐づけていたのですが、比較的良さそうなモノになりそうな気がします。
メニューバーにサービスのアイコンが表示されているだけで、ちょっと気分が上がりそうな気がしています。 :upside_down:
表示している画像を適切なモノに変えてあげれば、Archiveして社内に配っても良いかもしれません。

以上、ありがとうございました。

明日は@masadooonさんです!!Rettyワッショイ!!


  1. 使用している画像などはすべて仮のデータで、正式なものではありません 

  2. 記事内で作成したアプリは導入に至ったわけではなく、単に制作しただけです 

  3. 写真のモデルは弊社の @tkngue くんです 

  4. 主観