Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
マルチウィンドウ実践ガイド
Search
tatsubee
October 05, 2025
320
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
マルチウィンドウ実践ガイド
tatsubee
October 05, 2025
More Decks by tatsubee
See All by tatsubee
Create Spatial Photo with ImagePresentationComponent
shoryuyamamoto
0
110
pixivのリアーキテクチャにおける The Composable Architecter活用
shoryuyamamoto
0
210
pixivアプリは変化する
shoryuyamamoto
0
1.2k
マルチウィンドウでアプリケーションの表現を拡張する
shoryuyamamoto
1
430
【After iOSDC LT Night〜ピクシブ×日経×タイミー〜】実装!Interactive Widgets
shoryuyamamoto
0
80
SwiftPM マルチモジュール構成への第一歩
shoryuyamamoto
0
3.3k
TCA with UIKit [TCAでわいわいLT会]
shoryuyamamoto
1
1.5k
Dart Macrosに願いを [YOUTRUST x ゆめみ Flutter LT会@渋谷 #4]
shoryuyamamoto
0
910
riverpodを理解したい
shoryuyamamoto
0
210
Featured
See All Featured
Odyssey Design
rkendrick25
PRO
2
750
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
330
GitHub's CSS Performance
jonrohan
1033
470k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
232
55k
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.7k
エンジニアに許された特別な時間の終わり
watany
108
250k
HDC tutorial
michielstock
2
790
A Modern Web Designer's Workflow
chriscoyier
698
190k
AI Search: Where Are We & What Can We Do About It?
aleyda
0
7.8k
Designing for humans not robots
tammielis
254
26k
Documentation Writing (for coders)
carmenintech
77
5.4k
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
250
Transcript
ϚϧνΟϯυ࣮ફΨΠυ tatsubee ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
• iOS൛pixivΞϓϦ։ൃத • Ԭੜ·ΕɾԬҭͪɾ౦ژࡏॅ • झຯ: ͓ֆඳ͖ tatsubee QSPEVDUʛQJYJW
ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
iPadOS 26͔Βͷ ϚϧνΟϯυ֓ཁ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
iPadOS 26͔ΒͷϚϧνΟϯυ֓ཁ iPadOS 26 ~ • ϢʔβʔͷબʹΑͬͯϑϧεΫϦʔϯɺ͘͠Οϯυ ׂͷͲͪΒ͔ͷݟͨͰදࣔ͞ΕΔ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
iPadOS 26͔ΒͷϚϧνΟϯυ֓ཁ iPadOS 26 ~ • ϢʔβʔͷબʹΑͬͯϑϧεΫϦʔϯɺ͘͠Οϯυ ׂͷͲͪΒ͔ͷݟͨͰදࣔ͞ΕΔ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
iPadOS 26͔ΒͷϚϧνΟϯυ֓ཁ iPadOS 26 ~ • ϢʔβʔͷબʹΑͬͯϑϧεΫϦʔϯɺ͘͠Οϯυ ׂͷͲͪΒ͔ͷݟͨͰදࣔ͞ΕΔ • ΟϯυׂදࣔͷαΠζॊೈʹมಈͤ͞Δ͜ͱ͕Ͱ͖ɺҐ
ஔࣗ༝ʹஔ͢Δ͜ͱ͕Ͱ͖Δ • (ࠓ·Ͱͱಉ͘͡)ΞϓϦͷΟϯυෳ։͘͜ͱ͕Ͱ͖Δ • Οϯυͷຕࠓ·ͰҎ্ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
iPadOS 26͔ΒͷϚϧνΟϯυ֓ཁ iPadOS 26 ~ • ϢʔβʔͷબʹΑͬͯϑϧεΫϦʔϯɺ͘͠Οϯυ ׂͷͲͪΒ͔ͷݟͨͰදࣔ͞ΕΔ • ΟϯυׂදࣔͷαΠζॊೈʹมಈͤ͞Δ͜ͱ͕Ͱ͖ɺҐ
ஔࣗ༝ʹஔ͢Δ͜ͱ͕Ͱ͖Δ • (ࠓ·Ͱͱಉ͘͡)ΞϓϦͷΟϯυෳ։͘͜ͱ͕Ͱ͖Δ • Οϯυͷຕࠓ·ͰҎ্ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
iPadOS 26͔ΒͷϚϧνΟϯυ֓ཁ iPadOS 26 ~ • ϢʔβʔͷબʹΑͬͯϑϧεΫϦʔϯɺ͘͠Οϯυ ׂͷͲͪΒ͔ͷݟͨͰදࣔ͞ΕΔ • ΟϯυׂදࣔͷαΠζॊೈʹมಈͤ͞Δ͜ͱ͕Ͱ͖ɺҐ
ஔࣗ༝ʹஔ͢Δ͜ͱ͕Ͱ͖Δ • (ࠓ·Ͱͱಉ͘͡)ΞϓϦͷΟϯυෳ։͘͜ͱ͕Ͱ͖Δ • Οϯυͷຕࠓ·ͰҎ্ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF J1BE04ʹ৴͍ͯ͠Δͯ͢ͷΞϓϦ͕ దԠ͖͢ཁ
iPadOS 26͔ΒͷϚϧνΟϯυ֓ཁ iPadOS 26 ~ • ϢʔβʔͷબʹΑͬͯϑϧεΫϦʔϯɺ͘͠Οϯυ ׂͷͲͪΒ͔ͷݟͨͰදࣔ͞ΕΔ • ΟϯυׂදࣔͷαΠζॊೈʹมಈͤ͞Δ͜ͱ͕Ͱ͖ɺҐ
ஔࣗ༝ʹஔ͢Δ͜ͱ͕Ͱ͖Δ • (ࠓ·Ͱͱಉ͘͡)ΞϓϦͷΟϯυෳ։͘͜ͱ͕Ͱ͖Δ • Οϯυͷຕࠓ·ͰҎ্ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF J1BE04ʹ৴͍ͯ͠ΔΞϓϦ͕ରԠͰ͖Δͱ ʮڧΈʯͱͳΔମݧ
iPadOS 26͔ΒͷϚϧνΟϯυ֓ཁ ͱ͍͏Θ͚Ͱ • ΟϯυׂදࣔͷదԠ • ৽͍͠Οϯυͷ։͖ํ ʹযΛͯͯɺϚϧνΟϯυͷ࣮ફํ๏Λݟ͍͖ͯ·͢ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυׂදࣔʹదԠ͢Δ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυׂදࣔʹదԠ͢Δ Οϯυׂදࣔͷࡍʹ࣍ͷ2Λߟྀ͍ͨ͠ • Οϯυίϯτϩʔϧ • ΟϯυαΠζ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυׂදࣔʹదԠ͢Δ Οϯυׂදࣔͷࡍʹ࣍ͷ2Λߟྀ͍ͨ͠ • Οϯυίϯτϩʔϧ • ΟϯυαΠζ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ ΟϯυׂදࣔͷࡍʹɺToolbarྖҬͷઌʹΟϯυί ϯτϩʔϧ͕දࣔ͞ΕΔɻ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ ΟϯυׂදࣔͷࡍʹɺToolbarྖҬͷઌʹΟϯυί ϯτϩʔϧ͕දࣔ͞ΕΔɻ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF ˢ͜Ε
ΟϯυίϯτϩʔϧΛߟྀ͢Δ ΟϯυׂදࣔͷࡍʹɺToolbarྖҬͷઌʹΟϯυί ϯτϩʔϧ͕දࣔ͞ΕΔɻ Οϯυίϯτϩʔϧ͕ίϯςϯπͱॏͳΒͳ͍Α͏ʹ͠Α͏ʂ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF ˢ͜Ε
ΟϯυίϯτϩʔϧΛߟྀ͢Δ toolbarΛ༻͍ͯ͠Δ߹: ToolbarItem(placement:content:) ࣗಈతʹΟϯυίϯτϩʔϧΛආ͚ͯ͘ΕΔͷͰɺ ಛʹҙࣝ͢Δඞཁͳ͍ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ toolbarΛ༻͍ͯ͠Δ߹: Text(" ↖︎ ࠨ্ʹ") .toolbar { ToolbarItem(placement: .topBarLeading) {
Text("1") } ToolbarItem(placement: .topBarLeading) { Text("2") } ToolbarItem(placement: .topBarLeading) { Text("3") } ToolbarItem(placement: .topBarTrailing) { Image(systemName: "ellipsis") } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ toolbarΛ༻͍ͯ͠Δ߹: Text(" ↖︎ ࠨ্ʹ") .toolbar { ToolbarItem(placement: .topBarLeading) {
Text("1") } ToolbarItem(placement: .topBarLeading) { Text("2") } ToolbarItem(placement: .topBarLeading) { Text("3") } ToolbarItem(placement: .topBarTrailing) { Image(systemName: "ellipsis") } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ toolbarΛ༻͍ͯ͠ͳ͍߹: ΟϯυίϯτϩʔϧͱॏͳΒͳ͍Α͏ʹ खಈͰௐ͢Δඞཁ͕͋Δ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ toolbarΛ༻͍ͯ͠ͳ͍߹: ΟϯυίϯτϩʔϧͱॏͳΒͳ͍Α͏ʹ खಈͰௐ͢Δඞཁ͕͋Δ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ ΟϯυίϯτϩʔϧͷҐஔΛऔಘ͢Δํ๏ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ ΟϯυίϯτϩʔϧͷҐஔΛऔಘ͢Δํ๏ → GeometoryReader ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ ΟϯυίϯτϩʔϧͷҐஔΛऔಘ͢Δํ๏ GeometryReader { proxy in Rectangle() .fill(.red) .frame( width:
proxy.containerCornerInsets.topLeading.width, height: proxy.containerCornerInsets.topLeading.height ) .ignoresSafeArea() } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ ΟϯυίϯτϩʔϧͷҐஔΛऔಘ͢Δํ๏ GeometryReader { proxy in Rectangle() .fill(.red) .frame( width:
proxy.containerCornerInsets.topLeading.width, height: proxy.containerCornerInsets.topLeading.height ) .ignoresSafeArea() } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ ΟϯυίϯτϩʔϧͷҐஔΛऔಘ͢Δํ๏ GeometryReader { proxy in Rectangle() .fill(.red) .frame( width:
proxy.containerCornerInsets.topLeading.width, height: proxy.containerCornerInsets.topLeading.height ) .ignoresSafeArea() } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF Οϯυίϯτϩʔϧͷ Ґஔ͕औಘͰ͖ͨ🎉
ҙ⚠
ΟϯυίϯτϩʔϧΛߟྀ͢Δ ΟϯυίϯτϩʔϧͷҐஔΛऔಘ͢Δํ๏ GeometryReader { proxy in Rectangle() .fill(.red) .frame( width:
proxy.containerCornerInsets.topLeading.width, height: proxy.containerCornerInsets.topLeading.height ) .ignoresSafeArea() } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ ηʔϑΤϦΞͷӨڹΛड͚ͯ ΟϯυίϯτϩʔϧҠಈ͢Δʂ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ ΟϯυίϯτϩʔϧͷҐஔΛऔಘ͢Δํ๏ GeometryReader { proxy in Rectangle() .fill(.red) .frame( width:
proxy.containerCornerInsets.topLeading.width, height: proxy.containerCornerInsets.topLeading.height ) .ignoresSafeArea() } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ ΟϯυίϯτϩʔϧͷҐஔΛऔಘ͢Δํ๏ GeometryReader { proxy in Rectangle() .fill(.red) .frame( width:
proxy.containerCornerInsets.topLeading.width, height: proxy.containerCornerInsets.topLeading.height + proxy.safeAreaInsets.top ) .ignoresSafeArea() } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ ΟϯυίϯτϩʔϧͷҐஔΛऔಘ͢Δํ๏ GeometryReader { proxy in Rectangle() .fill(.red) .frame( width:
proxy.containerCornerInsets.topLeading.width, height: proxy.containerCornerInsets.topLeading.height + proxy.safeAreaInsets.top ) .ignoresSafeArea() } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ ͨͿΜ࣮༻తͳίʔυ GeometryReader { proxy in Text("͜͜ʹʂ") .padding(.leading, proxy.containerCornerInsets.topLeading.width) .animation(.default,
value: proxy.containerCornerInsets) } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ ͨͿΜ࣮༻తͳίʔυ GeometryReader { proxy in Text("͜͜ʹʂ") .padding(.leading, proxy.containerCornerInsets.topLeading.width) .animation(.default,
value: proxy.containerCornerInsets) } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ UIKitͷ߹ @available(iOS 26.0, tvOS 26.0, *) @MainActor @preconcurrency public
func layoutGuide( for region: UIView.LayoutRegion ) -> UILayoutGuide ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
ΟϯυίϯτϩʔϧΛߟྀ͢Δ UIKitͷ߹ NSLayoutConstraint.activate([ uiView.topAnchor.constraint( equalTo: view.safeAreaLayoutGuide.topAnchor ), uiView.leadingAnchor.constraint( equalTo: view.layoutGuide(for:
.safeArea(cornerAdaptation: .horizontal)).leadingAnchor ), ]) ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ ৽͍͠Οϯυͷ։͖ํ ver. OpenWindowAction ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ ৽͍͠Οϯυͷ։͖ํ ver. OpenWindowAction @main struct iPadMultiWindowApp: App { var
body: some Scene { WindowGroup { ContentView() } WindowGroup(id: "ID") { SomeView() } } } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ ৽͍͠Οϯυͷ։͖ํ ver. OpenWindowAction struct ContentView: View { @Environment(\.openWindow) var
openWindow var body: some View { Button("৽͍͠ΟϯυΛ։͘") { openWindow(id: "ID") } } } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ ৽͍͠Οϯυͷ։͖ํ ver. SwiftUI struct ContentView: View { @Environment(\.openWindow) var
openWindow var body: some View { Button("৽͍͠ΟϯυΛ։͘") { openWindow(id: "ID") } } } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ ৽͍͠Οϯυͷ։͖ํ ver. Drag & Drop struct ContentView: View {
var body: some View { Image(resource) } } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ ৽͍͠Οϯυͷ։͖ํ ver. Drag & Drop struct ContentView: View {
var body: some View { Image(resource) .onDrag { } } } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ ৽͍͠Οϯυͷ։͖ํ ver. Drag & Drop struct ContentView: View {
var body: some View { Image(resource) .onDrag { let userActivity = NSUserActivity( activityType: "dev.shoryu.MultiWindowExample.openWindow" ) userActivity.targetContentIdentifier = "targetContentIdentifier" return NSItemProvider(object: userActivity) } } } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ ৽͍͠Οϯυͷ։͖ํ ver. Drag & Drop WindowGroup(id: Self.activityType) { TargetView()
} .handlesExternalEvents(matching: ["targetContentIdentifier"]) ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ iPadOS 26ͰແͷΟϯυΛ։͘͜ͱ͕Ͱ͖Δ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ iPadOS 26ͰແͷΟϯυΛ։͘͜ͱ͕Ͱ͖Δ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ iPadOS 26ͰແͷΟϯυΛ։͘͜ͱ͕Ͱ͖Δ • 10ຕఔͰiPadͷڍಈ͕ॏ͘… • ࣮༻తʹ5ຕఔʁ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ iPadOS 26ͰແͷΟϯυΛ։͘͜ͱ͕Ͱ͖Δ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ iPadOS 26ͰແͷΟϯυΛ։͘͜ͱ͕Ͱ͖Δ → Կ͕ͲͷΟϯυͳͷ͔Λಛఆ͘͢͢͠Δඞཁ͕͋Δ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ iPadOS 26ͰແͷΟϯυΛ։͘͜ͱ͕Ͱ͖Δ → Կ͕ͲͷΟϯυͳͷ͔Λࣝผ͘͢͢͠Δඞཁ͕͋Δ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ ΟϯυʹλΠτϧΛ͚ͭΔ WindowGroup(id: "ID") { SomeView() .navigationBarTitle("ΟϯυͷλΠτϧ") } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
৽͍͠ΟϯυΛ։͘ ΟϯυʹλΠτϧΛ͚ͭΔ WindowGroup(id: "ID") { SomeView() .navigationBarTitle("ΟϯυͷλΠτϧ") } ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
·ͱΊ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
·ͱΊ • iPadOS 26͔Βͯ͢ͷΞϓϦΟϯυׂදࣔ͞ΕΔΑ͏ ʹͳͬͨΑ • ेͳίϯςϯπΛදࣔͰ͖ΔΑ͏ʹɺ ΟϯυίϯτϩʔϧͷྖҬͱΟϯυαΠζͷॊೈੑʹ ͯ͢ͷΞϓϦͰؾΛ͚ͭΑ͏ •
ϚϧνΟϯυΛ͍͜ͳͤΑΓྑ͍iPadΞϓϦମݧ͕ٻ Ͱ͖Δ͔ʂ ϚϧνΟϯυ࣮ફΨΠυʛUBUTVCFF
͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ