Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Swift UIで始めるmacOSメニューバーアプリ

Swift UIで始めるmacOSメニューバーアプリ

株式会社ヌーラボ

April 22, 2022
Tweet

More Decks by 株式会社ヌーラボ

Other Decks in Technology

Transcript

  1. SwiftUI • એݴܕγϯλοΫεͳUIϑϨʔϜϫʔΫ • macOS, iOS, iPadOS, tvOS, watchOSͰ࢖͑Δ •

    XcodeͰͷϓϨϏϡʔ΍σβΠϯαϙʔτ AppKit • MVCͳUIϑϨʔϜϫʔΫ • macOSͰ࢖͑Δ • NSʙͱ͍͏pre fi x͕෇͍͍ͯΔ NBD04ʙ NBD04ʙ /FYU4UFQʜ
  2. DemoApp.swift import SwiftUI @main struct DemoApp: App { var body:

    some Scene { WindowGroup { ContentView() } } } import SwiftUI struct ContentView: View { var body: some View { Text("Hello, world!") .padding() } } ContentView.swift
  3. @NSApplicationDelegateAdaptor import SwiftUI @main struct DemoApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var

    delegate var body: some Scene { WindowGroup { ContentView() } } } class AppDelegate: NSObject, NSApplicationDelegate { } ΞϓϦͷىಈ௚ޙʹݺ͹ΕΔϝιουͳͲ͕࢖͑ΔΑ͏ʹͳΔ
  4. class AppDelegate: NSObject, NSApplicationDelegate { } private var statusItem: NSStatusItem?

    func applicationDidFinishLaunching(_ notification: Notification) { statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) let button = statusItem!.button! button.image = NSImage(systemSymbolName: "leaf", accessibilityDescription: nil) } 4'4ZNCPMT͕࢖͑Δ ΞϓϦͷىಈ௚ޙʹݺ͹ΕΔϝιου
  5. class AppDelegate: NSObject, NSApplicationDelegate { } private var statusItem: NSStatusItem?

    private var popover: NSPopover? func applicationDidFinishLaunching(_ notification: Notification) { statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) let button = statusItem!.button! button.image = NSImage(systemSymbolName: "leaf", accessibilityDescription: nil) button.action = #selector(showPopover) } @objc func showPopover(_ sender: NSStatusBarButton) { if popover == nil { let popover = NSPopover() popover.contentViewController = NSHostingController(rootView: ContentView()) self.popover = popover } popover?.show(relativeTo: sender.bounds, of: sender, preferredEdge: NSRectEdge.maxY) } 4XJGU6*Λϗετͯ͘͠ΕΔ"QQ,JUͷίϯτϩʔϥ
  6. class AppDelegate: NSObject, NSApplicationDelegate { } private var statusItem: NSStatusItem?

    private var popover: NSPopover? func applicationDidFinishLaunching(_ notification: Notification) { NSApp.windows.forEach{ $0.close() } NSApp.setActivationPolicy(.accessory) statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) let button = statusItem!.button! button.image = NSImage(systemSymbolName: "leaf", accessibilityDescription: nil) button.action = #selector(showPopover) } @objc func showPopover(_ sender: NSStatusBarButton) { if popover == nil { let popover = NSPopover() popover.contentViewController = NSHostingController(rootView: ContentView()) popover.behavior = .transient popover.animates = false self.popover = popover } popover?.show(relativeTo: sender.bounds, of: sender, preferredEdge: NSRectEdge.maxY) NSApp.activate(ignoringOtherApps: true) } ΞϓϦىಈதʹ%PDL͔ΒΞΠίϯΛফ͢ ΞϓϦىಈ௚ޙͷ΢Οϯυ΢Λશ෦ফ͢ ΞϓϦҎ֎ͷྖҬͷΫϦοΫͰϙοϓΞοϓ͕ด͡ΔΑ͏ʹ͢Δ ΞϓϦΛΞΫςΟϒʹ͠ͳ͍ͱ͏·͘ด͡ͳ͍
  7. private var statusItem: NSStatusItem? @main struct DemoApp: App { #if

    os(macOS) @NSApplicationDelegateAdaptor(AppDelegate.self) var delegate #endif var body: some Scene { WindowGroup { ContentView() } } } #if os(macOS) class AppDelegate: NSObject, NSApplicationDelegate { … } #endif
  8. ContentView.swift import SwiftUI struct ContentView: View { var body: some

    View { Text("Hello, world!") .padding() } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
  9. struct ContentView: View { var body: some View { Button

    { print("pushed") } label: { HStack { Image(systemName: "face.smiling") Text("Hello, world!") } } .padding() } }
  10. struct ContentView: View { @State var point: CGFloat = -1

    var body: some View { let gradient = LinearGradient(gradient: Gradient(colors: [.blue, .green, .blue]), startPoint: UnitPoint(x: point, y: 0), endPoint: UnitPoint(x: 1.0 + point, y: 0)) VStack { Capsule() .fill(gradient) .animation(Animation.linear(duration: 2) .repeatForever(autoreverses: false), value: point) .onAppear { point = 1 } .frame(width: 200, height: 10, alignment: .leading) .padding() Button { print("pushed") } label: { HStack { Image(systemName: "face.smiling") Text("Hello, world!") } } } .padding() } }
  11. ӈΫϦοΫϝχϡʔΛͭ͘Δ @objc func showPopover(_ sender: NSStatusBarButton) { guard let event

    = NSApp.currentEvent else { return } if event.type == NSEvent.EventType.rightMouseUp { let menu = NSMenu() menu.addItem( withTitle: NSLocalizedString("Preference", comment: "Show preferences window"), action: #selector(openPreferencesWindow), keyEquivalent: "" ) menu.addItem(.separator()) menu.addItem( withTitle: NSLocalizedString("Quit", comment: "Quit app"), action: #selector(terminate), keyEquivalent: "" ) statusItem?.popUpMenu(menu) return } ɿ ࣄલʹ button.sendAction(on: [.leftMouseUp, .rightMouseUp]) ͕ඞཁ
  12. TextFieldͳͲͰΩʔϘʔυγϣʔτΧοτΛ༗ޮʹ͢Δ @main struct FreeeApp: App { #if os(macOS) @NSApplicationDelegateAdaptor(AppDelegate.self) var

    delegate #endif var body: some Scene { #if os(macOS) WindowGroup { // empty }.commands { TextEditingCommands() } Settings { SettingsView() } #else WindowGroup { ContentView(store: AppStore()) } #endif } } ςΩετฤूܥͷϏϧτΠϯίϚϯυ܈
  13. ΞϓϦͷ഑෍ (ඇAppStore) • macOS 10.15 (Catalina) Ҏ߱͸Appleͷެূ (Notarization) Λ෇͚ ͍ͯͳ͍ΞϓϦ͸ىಈͤͮ͞Β͘ͳ͍ͬͯΔ

    • XcodeͰProduct→Archive • OrganizerΛ։͘ • Distribute App → Developer ID → Upload • ͠͹Β͘଴ͭ • Distribute App → Developer ID • Successfully notarizedͱݴΘΕΔͷͰExportΛΫϦοΫ