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
280
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
100
pixivのリアーキテクチャにおける The Composable Architecter活用
shoryuyamamoto
0
210
pixivアプリは変化する
shoryuyamamoto
0
1.1k
マルチウィンドウでアプリケーションの表現を拡張する
shoryuyamamoto
1
400
【After iOSDC LT Night〜ピクシブ×日経×タイミー〜】実装!Interactive Widgets
shoryuyamamoto
0
71
SwiftPM マルチモジュール構成への第一歩
shoryuyamamoto
0
3.3k
TCA with UIKit [TCAでわいわいLT会]
shoryuyamamoto
1
1.4k
Dart Macrosに願いを [YOUTRUST x ゆめみ Flutter LT会@渋谷 #4]
shoryuyamamoto
0
890
riverpodを理解したい
shoryuyamamoto
0
190
Featured
See All Featured
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
250
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
720
Test your architecture with Archunit
thirion
1
2.3k
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.7k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
231
23k
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
130
The State of eCommerce SEO: How to Win in Today's Products SERPs - #SEOweek
aleyda
2
11k
Tips & Tricks on How to Get Your First Job In Tech
honzajavorek
1
530
[SF Ruby Conf 2025] Rails X
palkan
2
1.1k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
3.3k
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
200
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
360
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
͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ