サンプルはたくさんあるけれど・・・
Macのメニューバーに常駐するアプリケーションをSwiftUIで作るサンプルをいくつか探して試してみたのですが、現在のXcodeで作成できるSwiftアプリケーションのテンプレートだとうまくいかなかったため、さらにいろいろと駆けずり回ってどうにかサンプルが作れたので手順をメモしておきます。
プロジェクト作成
macOSアプリとして作成します。
Interfaceを Storyboard
で作成します。
作成されたプロジェクト
ストーリーボードの修正
Mainを開いて、Window Controller Scene
を選択して、delete
キーで削除します。
アイコンの追加
Assetsを開いて、メニューバーに表示するアイコンを追加します。
追加されたアイコン
info.plistの修正
Application is agent (UIElement)
を YES
に設定します。
ソースの修正
StatusBarControllerの追加
メニューバーの表示&制御を行うコントローラを追加します。
import Foundation
import AppKit
class StatusBarController {
private var statusBar: NSStatusBar
private var statusItem: NSStatusItem
private var popover: NSPopover
init(_ popover: NSPopover) {
self.popover = popover
statusBar = NSStatusBar.init()
// Creating a status bar item having a fixed length
statusItem = statusBar.statusItem(withLength: 28.0)
if let statusBarButton = statusItem.button {
// 追加したアイコン名を設定
statusBarButton.image = #imageLiteral(resourceName: "icon")
statusBarButton.image?.size = NSSize(width: 18.0, height: 18.0)
statusBarButton.image?.isTemplate = true
statusBarButton.action = #selector(togglePopover(sender:))
statusBarButton.target = self
}
}
@objc func togglePopover(sender: AnyObject) {
if(popover.isShown) {
hidePopover(sender)
}
else {
showPopover(sender)
}
}
func showPopover(_ sender: AnyObject) {
if let statusBarButton = statusItem.button {
popover.show(relativeTo: statusBarButton.bounds, of: statusBarButton, preferredEdge: NSRectEdge.maxY)
}
}
func hidePopover(_ sender: AnyObject) {
popover.performClose(sender)
}
}
AppDelegateの修正
import Cocoa
import SwiftUI
@main
class AppDelegate: NSObject, NSApplicationDelegate {
var popover = NSPopover.init()
var statusBar: StatusBarController?
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Create the SwiftUI view that provides the contents
let contentView = ContentView()
// Set the SwiftUI's ContentView to the Popover's ContentViewController
popover.contentSize = NSSize(width: 360, height: 360)
popover.contentViewController = NSHostingController(rootView: contentView)
// Create the Status Bar Item with the above Popover
statusBar = StatusBarController.init(popover)
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
Viewを作成する
メニューバーに表示されたアイコンをクリックしたときに表示する画面を作成します。
import SwiftUI
struct ContentView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
.background(Color.yellow)
.foregroundColor(Color.blue)
.frame(width: 300, height: 150)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
実行結果
実行した結果です(暗くてわかりずらいですが)
まとめ
SwiftUIによる画面の開発が容易になったため、iOSだけでなくMacOSでなにかアプリを作ってみようかなと思いメニューバーアプリケーションとしてユーティリティーの作り方をいろいろと調べてみたのですが、年代やらXcodeのバージョンによっていろいろと作りが変わるようで、だいぶ沼化しました。
この作り方ももしかしたら他のやり方があるのかもしれませんが、どなたかのモチベーションを刺激できたら幸いですw
今回のサンプルはGitHubでも公開しています。
https://github.com/6in/MenuBarAppSample