Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Swift UIで始めるmacOSメニューバーアプリ
Search
株式会社ヌーラボ
PRO
April 22, 2022
Technology
1.2k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Swift UIで始めるmacOSメニューバーアプリ
株式会社ヌーラボ
PRO
April 22, 2022
More Decks by 株式会社ヌーラボ
See All by 株式会社ヌーラボ
製造業の設計・生産技術向け Backlog入門
nulabinc
PRO
0
19
配布資料_ヌーラボの情シスがおすすめする_問い合わせ対応を削減するBacklog活用術.pdf
nulabinc
PRO
0
120
Bits Agent Builder の⼊⾨と活⽤事例
nulabinc
PRO
0
3.2k
Getting Started with Bits Agent Builder: Real-World Use Cases
nulabinc
PRO
0
83
The_Evolution_of_Bits_AI_SRE.pdf
nulabinc
PRO
0
420
進化するBits AI SREと私と組織
nulabinc
PRO
4
1.3k
実践 Datadog MCP Server
nulabinc
PRO
4
1k
全社横断PjM⽀援チーム “PEaS”の取り組みと プロジェクトマネジメント でのAI活⽤について
nulabinc
PRO
0
260
Datadog の RBAC のすべて
nulabinc
PRO
4
1k
Other Decks in Technology
See All in Technology
WasmをRustにする
sansantech
PRO
0
250
SAENM ~Jupyterで残すPoCの試行錯誤、未来の自分と仲間を助ける記録!~
terapyon
0
110
エンタメ企業のM&Aを支えるデータ基盤 ― 統合から店舗のリアルタイム分析まで / Data Engineering Summit 2026
genda
0
170
Persisting Massive Data with Rails ~Designing for 60 billion rows, and fighting the bottlenecks~ Railsで挑む大量データ永続化 〜600億件を永続化するための設計とボトルネックとの戦い〜
occhi
0
730
Benchmarking Disaggregated Serving on vLLM + Dynamo with a Real Coding-Agent Workload
nttcom
1
190
Rustuna: Rustによる高速なOptuna実装
pfn
PRO
0
190
Temporal 最高!でも改めて「日付」について調べたら人類の負債すぎる件
riyaamemiya
0
150
Refining Active Record
shugo
0
150
会社説明資料
persolserverworks
0
9.8k
How to Design Configuration for Rails Applications
willnet
1
780
ブラウザとサーバーから考えるAI時代のフロントエンド監視
shogo4131
3
940
AI コーディングと人間の認知スループット / CTO Night & Day 2026 Morning Session / AI Coding and Human Cognitive Throughput
prog893
4
1.1k
Featured
See All Featured
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
3.1k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
2
2.2k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
53k
Between Models and Reality
mayunak
4
500
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
990
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
450
Six Lessons from altMBA
skipperchong
29
4.5k
The Power of CSS Pseudo Elements
geoffreycrofte
82
6.6k
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
330
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
50
10k
HDC tutorial
michielstock
2
960
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
410
Transcript
20224݄22 NuCon 2022 Spring দຊ༟ೋ Swift UIͰ࢝ΊΔ macOSϝχϡʔόʔΞϓϦ
ͭͬͨ͘ΞϓϦ
FreeeͷۈଵΞϓϦ ग़ۈͱୀۈΛߦ͏͚ͩͷγϯϓϧͳΞϓϦ (ψʔϥϘ͚ࣾͩͰத)
SwiftUI
SwiftUI • એݴܕγϯλοΫεͳUIϑϨʔϜϫʔΫ • macOS, iOS, iPadOS, tvOS, watchOSͰ͑Δ •
XcodeͰͷϓϨϏϡʔσβΠϯαϙʔτ AppKit • MVCͳUIϑϨʔϜϫʔΫ • macOSͰ͑Δ • NSʙͱ͍͏pre fi x͕͍͍ͯΔ NBD04ʙ NBD04ʙ /FYU4UFQʜ
Ψϫͷ෦4XJGU6*Ͱ࡞Εͳ͍ /41PQPWFS 4XJGU6*Ͱ࡞Εͳ͍ /44UBUVT*UFN/44UBUVT#BS#VUUPO 7JFX 1PQPWFS
ϝχϡʔόʔͷΫϦοΫͰ SwiftUIͷϏϡʔΛϙοϓΞοϓͤ͞Δ·Ͱ
None
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
@NSApplicationDelegateAdaptor import SwiftUI @main struct DemoApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var
delegate var body: some Scene { WindowGroup { ContentView() } } } class AppDelegate: NSObject, NSApplicationDelegate { } ΞϓϦͷىಈޙʹݺΕΔϝιουͳͲ͕͑ΔΑ͏ʹͳΔ
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͕͑Δ ΞϓϦͷىಈޙʹݺΕΔϝιου
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ͷίϯτϩʔϥ
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͔ΒΞΠίϯΛফ͢ ΞϓϦىಈޙͷΟϯυΛશ෦ফ͢ ΞϓϦҎ֎ͷྖҬͷΫϦοΫͰϙοϓΞοϓ͕ด͡ΔΑ͏ʹ͢Δ ΞϓϦΛΞΫςΟϒʹ͠ͳ͍ͱ͏·͘ด͡ͳ͍
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
SwiftUIΛ؆୯ʹհ
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() } }
struct ContentView: View { var body: some View { Button
{ print("pushed") } label: { HStack { Image(systemName: "face.smiling") Text("Hello, world!") } } .padding() } }
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() } }
Tips
ӈΫϦοΫϝχϡʔΛͭ͘Δ @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]) ͕ඞཁ
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 } } ςΩετฤूܥͷϏϧτΠϯίϚϯυ܈
ΞϓϦͷ (ඇAppStore) • macOS 10.15 (Catalina) Ҏ߱Appleͷެূ (Notarization) Λ͚ ͍ͯͳ͍ΞϓϦىಈͤͮ͞Β͘ͳ͍ͬͯΔ
• XcodeͰProduct→Archive • OrganizerΛ։͘ • Distribute App → Developer ID → Upload • ͠Βͭ͘ • Distribute App → Developer ID • Successfully notarizedͱݴΘΕΔͷͰExportΛΫϦοΫ
ඇදࣔAppͷ৴ • Unlisted app distribution • AppStoreʹ͋Δ͚ͲϦϯΫΛΒͳ͍ͱ͑ͳ͍ • 20221݄͝Ζ͔Β͑ΔΑ͏ʹͳͬͨ •
ࢼͯ͠Έ͍ͨ https://developer.apple.com/jp/support/unlisted-app-distribution/
4XJGU6*ͰϝχϡʔόʔΞϓϦΛ࡞ΕΔ