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
330
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
220
pixivアプリは変化する
shoryuyamamoto
0
1.2k
マルチウィンドウでアプリケーションの表現を拡張する
shoryuyamamoto
1
440
【After iOSDC LT Night〜ピクシブ×日経×タイミー〜】実装!Interactive Widgets
shoryuyamamoto
0
82
SwiftPM マルチモジュール構成への第一歩
shoryuyamamoto
0
3.4k
TCA with UIKit [TCAでわいわいLT会]
shoryuyamamoto
1
1.5k
Dart Macrosに願いを [YOUTRUST x ゆめみ Flutter LT会@渋谷 #4]
shoryuyamamoto
0
920
riverpodを理解したい
shoryuyamamoto
0
210
Featured
See All Featured
Six Lessons from altMBA
skipperchong
29
4.5k
Done Done
chrislema
186
16k
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
700
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
480
How to train your dragon (web standard)
notwaldorf
97
6.8k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.6k
Why You Should Never Use an ORM
jnunemaker
PRO
61
10k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.4k
Building the Perfect Custom Keyboard
takai
2
860
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
23k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
510
Accessibility Awareness
sabderemane
1
190
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
͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ