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
310
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
420
【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
200
Featured
See All Featured
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
220
My Coaching Mixtape
mlcsv
0
170
Visualization
eitanlees
152
17k
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
1
2.8k
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
420
Leo the Paperboy
mayatellez
8
1.9k
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
67
56k
Building Applications with DynamoDB
mza
96
7.1k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
WCS-LA-2024
lcolladotor
0
740
GitHub's CSS Performance
jonrohan
1033
470k
The browser strikes back
jonoalderson
0
1.4k
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
͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ