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
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
200
Mobile First: as difficult as doing things right
swwweet
225
10k
Rails Girls Zürich Keynote
gr2m
96
14k
sira's awesome portfolio website redesign presentation
elsirapls
0
270
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
720
Darren the Foodie - Storyboard
khoart
PRO
3
3.4k
YesSQL, Process and Tooling at Scale
rocio
174
15k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
320
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
320
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.1k
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
61
44k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
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
͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ