iPadOSDC: Multiple Windows

473b12ebcfb79adfaef97796e94fc25c?s=47 Hiron
September 20, 2020

iPadOSDC: Multiple Windows

iOSDC Japan 2020の発表資料です。
スライド中のApple公式資料へのリンクは https://gist.github.com/hironytic/67757d4275e3b19509d892080d78b38c も参照

473b12ebcfb79adfaef97796e94fc25c?s=128

Hiron

September 20, 2020
Tweet

Transcript

  1. .VMUJQMF8JOEPXT J04%$+BQBO J1BE04%$

  2. J1BE04 5JNF   "QSJM  +VOF  J1IPOF04 J1IPOF04

    J04 J04 J1BE04 J1BE .VMUJUBTLJOH .VMUJQMF 8JOEPXT
  3. ࣗݾ঺հ w ͻΖΜʢҰٶߒڭʣ w 5XJUUFS(JU)VC2JJUB 4MJEF4IBSF4QFBLFS%FDL ˠIJSPOZUJD w גࣜձࣾ.FUB.P+J ಙౡۈ຿

  4. J1BE04 w J1BEઐ༻ͷ04 w جຊతʹJ04ͱҰॹʹόʔδϣϯΞοϓ͍ͯ͠Δ w ։ൃ͸J044%,Ͱ w ΞϓϦͷόΠφϦ΋J04J1BE04ڞ௨ w

    ։ൃऀ͔ΒݟΕ͹J04ͱͷڥք͸͋·Γͳ͍
  5. .VMUJQMF8JOEPXT ʜͳʹͦΕ͓͍͍͠ͷʁ

  6. .VMUJQMF8JOEPXT ෳ਺ͷεϖʔεͰಉ͡ΞϓϦΛ։͍ͯ࡞ۀ͢Δ IUUQTXXXBQQMFDPNKQJQBEPT ˠ04ʹ౥ࡌ͞Ε͍ͯΔ ʮϝϞʯΞϓϦͷಈ࡞ྫ

  7. None
  8. ʮϝϞʯͷಈ࡞  w ΞϓϦΞΠίϯͷϝχϡʔʮ͢΂ͯͷ΢Οϯυ΢Λදࣔʯ ʢ"QQ&YQPTÉʣ w "QQ&YQPTÉͷӈ্ʹ͋ΔʴϘλϯ͔Β৽͍͠΢Οϯυ΢ Λ։͘͜ͱ͕Ͱ͖Δ w ৽͍͠΢Οϯυ΢͸"QQεΠονϟʔʹฒΜͰɺΞϓϦΛ੾

    Γସ͑Δײ֮Ͱ΢Οϯυ΢Λ੾Γସ͑ΒΕΔ w 4QMJU7JFXͰಉ͡ΞϓϦͷผ΢Οϯυ΢Λฒ΂ΒΕΔ
  9. None
  10. ʮϝϞʯͷಈ࡞  w ϝϞͷΞΠςϜΛυϥοά͢Δ͜ͱͰผͷ΢Οϯυ΢Λ։ ͘͜ͱ͕Ͱ͖Δ w ը໘ͷࠨӈͷ୺ʹυϥοά͢Δͱ4QMJU7JFXͰɺ্୺ʹ υϥοά͢Δͱશը໘Ͱɺ৽͍͠΢Οϯυ΢͕։͔ΕΔ w ϝϞΞΠςϜ͔Β։͍ͨ΢Οϯυ΢͸ɺϝϞͷҰཡʹ໭Δ

    ͜ͱ͸ͳ͘ɺฤू͕ऴΘͬͨΒด͡Δ6*ʹͳ͍ͬͯΔ
  11. None
  12. ʮϝϞʯͷಈ࡞  w "QQεΠονϟʔ΍ɺ"QQ&YQPTÉͰ΢Οϯυ ΢Λ্ํ޲ʹεϫΠϓ͢Δͱɺ΢Οϯυ΢Λด͡ Δ͜ͱʹͳΔ ʢ͜Ε·ͰͷΞϓϦΛऴྃͤ͞Δͷͱಉ͡ૢ࡞ʣ w "QQ&YQPTÉʹดͨ͡΢Οϯυ΢Λ࠶ͼ։͘Ϙλ ϯ͕දࣔ͞ΕΔ

  13. ΞϓϦΛ.VMUJQMF 8JOEPXTʹରԠͤ͞Δ

  14. ༧උ஌ࣝ w ͭͷΞϓϦͷϓϩηε͸ैདྷ௨Γͭ w ͜͜·Ͱʮεϖʔεʯ΍ʮ΢Οϯυ΢ʯͱݺΜͰ ͖ͨ΋ͷ͸ʮγʔϯʯʹ૬౰ w લ໘ʹදࣔ͞Ε͍ͯͳ͍γʔϯ͸੾அ͞ΕͨΓɺ ͦ΋ͦ΋઀ଓ͞Εͯͳ͔ͬͨΓ͢Δ

  15. ొ৔ਓ෺૬ؔਤ 6*"QQMJDBUJPO "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF 6*8JOEPX

    7JFX$POUSPMMFS 6*7JFX$POUSPMMFS 7JFX 6*8JOEPX4DFOF 6*4DFOF /46TFS"DUJWJUZ /4*UFN1SPWJEFS 6*%SBH*UFN 6*4DFOF4FTTJPO ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ ੵΉ
  16. ʰ4FTTJPOTʱ w Ұཡը໘ͱৄࡉը໘͔ΒͳΔ γϯϓϧͳΞϓϦ w ৄࡉը໘Ͱελʔͷ෇͚֎͠ ͕Ͱ͖Δ w .VMUJQMF8JOEPXTʹະର ԠˠରԠ͍ͤͯ͘͞

    IUUQTHJUIVCDPNIJSPOZUJD.VMUJQMF8JOEPXT4BNQMF
  17. 4UFQ  γʔϯ͸؆୯ ͦ͏ɺJ04Ҏ߱ͷΈͳΒͶ

  18. 9DPEFͰ৽نϓϩδΣΫτ࡞੒ w ࣗಈతʹγʔϯʹରԠͨ͠ςϯϓϨʔτͰΞϓϦ ͕࡞ΒΕΔ ˒ J04ҎલΛαϙʔτͤ͞Α͏ͱͨ͠ΒίϯύΠϧΤ ϥʔʹͳͬͯϜΧͭ͘ w ͦͷରԠͨ͠ίʔυΛίϐϖ͢Δͱָ w

    J04Ҏલͷαϙʔτํ๏ʜʜαϙʔτ΍ΊΑ͏
  19. *OGPQMJTUʹ4DFOF.BOJGFTUΛ௥Ճ <key>UIApplicationSceneManifest</key> <dict> <key>UIApplicationSupportsMultipleScenes</key> <false/> <key>UISceneConfigurations</key> <dict> <key>UIWindowSceneSessionRoleApplication</key> <array> <dict>

    <key>UISceneConfigurationName</key> <string>Default Configuration</string> <key>UISceneDelegateClassName</key> <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string> <key>UISceneStoryboardFile</key> <string>Main</string> </dict> </array> </dict> </dict> ˡ1SPQFSUZ-JTUදࣔ 4PVSDF$PEFදࣔˠ
  20. "QQ%FMFHBUFʹϝιουΛ଍͢ class AppDelegate: UIResponder, UIApplicationDelegate { ... func application(_ application:

    UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } ... }
  21. 4DFOF%FMFHBUFΫϥεΛ࡞Δ class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func

    scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // Use this method to optionally configure and attach the // UIWindow `window` to the provided UIWindowScene `scene`. // If using a storyboard, the `window` property will automatically // be initialized and attached to the scene. // This delegate does not imply the connecting scene or session are // new (see `application:configurationForConnectingSceneSession` instead). guard let _ = (scene as? UIWindowScene) else { return } } func sceneDidDisconnect(_ scene: UIScene) { ...
  22. 6*4DFOF$POpHVSBUJPO 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ ొ৔ਓ෺૬ؔਤ 6*8JOEPX 7JFX$POUSPMMFS

    6*7JFX$POUSPMMFS 7JFX 6*8JOEPX4DFOF 6*4DFOF /46TFS"DUJWJUZ /4*UFN1SPWJEFS 6*%SBH*UFN 6*4DFOF4FTTJPO ੵΉ "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*"QQMJDBUJPO
  23. 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF ΫϥεΛࢦఆ ొ৔ਓ෺૬ؔਤ 6*8JOEPX 7JFX$POUSPMMFS 6*7JFX$POUSPMMFS 7JFX

    6*8JOEPX4DFOF 6*4DFOF /46TFS"DUJWJUZ /4*UFN1SPWJEFS 6*%SBH*UFN 6*4DFOF4FTTJPO ੵΉ "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO ੜ੒ͯ͠ฦ͢ 6*"QQMJDBUJPO
  24. ొ৔ਓ෺૬ؔਤ 6*8JOEPX 7JFX$POUSPMMFS 6*7JFX$POUSPMMFS 7JFX /46TFS"DUJWJUZ /4*UFN1SPWJEFS 6*%SBH*UFN 6*4DFOF4FTTJPO ੵΉ

    "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ 6*"QQMJDBUJPO 6*8JOEPX4DFOF 6*4DFOF
  25. ొ৔ਓ෺૬ؔਤ /46TFS"DUJWJUZ /4*UFN1SPWJEFS 6*%SBH*UFN 6*4DFOF4FTTJPO ੵΉ "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO 4DFOF%FMFHBUF

    6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ 6*8JOEPX 7JFX$POUSPMMFS 6*7JFX$POUSPMMFS 7JFX 6*"QQMJDBUJPO 6*8JOEPX4DFOF 6*4DFOF
  26. 4DFOF%FMFHBUF w ࠓ·Ͱ͸ը໘ͷϥΠϑαΠΫϧʹؔ͢Δ௨஌Λ "QQ%FMFHBUFͰड͚͍ͯͨ w ΞϓϦͭͷը໘Ͱ͸ͳ͘ͳΔͷͰ ը໘୯ҐʢγʔϯʣͰϥΠϑαΠΫϧ͕ҟͳΔ w ΞϓϦʢ6*"QQMJDBUJPOʣʹରͯ͠"QQ%FMFHBUFɺ γʔϯʢ6*4DFOFʣʹରͯ͠4DFOF%FMFHBUF

  27. 4DFOF%FMFHBUF "QQ%FMFHBUFͷҎԼͷϝιου͸ݺ͹Εͳ͘ͳΔ  BQQMJDBUJPO%JE#FDPNF"DUJWF @   BQQMJDBUJPO8JMM3FTJHO"DUJWF @ 

     BQQMJDBUJPO%JE&OUFS#BDLHSPVOE @   BQQMJDBUJPO8JMM&OUFS'PSFHSPVOE @   ͳͲ 4DFOF%FMFHBUFͷҎԼͷϝιου͕ݺ͹ΕΔΑ͏ʹͳΔ  TDFOF%JE#FDPNF"DUJWF @   TDFOF8JMM3FTJHO"DUJWF @   TDFOF%JE&OUFS#BDLHSPVOE @   TDFOF8JMM&OUFS'PSFHSPVOE @   ͳͲ ˞J04Ͱ͸ݺ͹ΕΔ
  28. 4DFOF%FMFHBUF "QQ%FMFHBUFͷ BQQMJDBUJPO @EJE'JOJTI-BVODIJOH8JUI0QUJPOT ͸ݺ͹ΕΔ͕ɺ ΢Οϯυ΢ͷߏஙͳͲ͸4DFOF%FMFHBUFͷ TDFOF @XJMM$POOFDU5PPQUJPOT Ͱߦ͏ func

    scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // Use this method to optionally configure and attach the // UIWindow `window` to the provided UIWindowScene `scene`. // If using a storyboard, the `window` property will automatically // be initialized and attached to the scene. // This delegate does not imply the connecting scene or session are // new (see `application:configurationForConnectingSceneSession` instead). guard let _ = (scene as? UIWindowScene) else { return } }
  29. "QQ%FMFHBUF͔Β4DFOF%FMFHBUF΁Ҿͬӽ͠ func applicationWillResignActive(_ application: UIApplication) { // Sent when the

    application is about to move from active to inactive state. // This can occur for certain types of temporary interruptions (such as an // incoming phone call or SMS message) or when the user quits the application // and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and invalidate // graphics rendering callbacks. Games should use this method to pause the game. } func applicationDidEnterBackground(_ application: UIApplication) { // Use this method to release shared resources, save user data, invalidate // timers, and store enough application state information to restore your // application to its current state in case it is terminated later. // If your application supports background execution, this method is called // instead of applicationWillTerminate: when the user quits. } func applicationWillEnterForeground(_ application: UIApplication) { ... var window: UIWindow?
  30. 4UFQ  ෳ਺ͷγʔϯΛαϙʔτʂ ઓ͍ͷ࢝·Γ

  31. *OGPQMJTUͷ4DFOF.BOJGFTUΛมߋ <key>UIApplicationSceneManifest</key> <dict> <key>UIApplicationSupportsMultipleScenes</key> <true/> <key>UISceneConfigurations</key> <dict> <key>UIWindowSceneSessionRoleApplication</key> <array> <dict>

    <key>UISceneConfigurationName</key> <string>Default Configuration</string> <key>UISceneDelegateClassName</key> <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string> <key>UISceneStoryboardFile</key> <string>Main</string> </dict> </array> </dict> </dict> ˡ1SPQFSUZ-JTUදࣔ 4PVSDF$PEFදࣔˠ
  32. ͜Ε͚ͩͰ࠷௿ݶͷಈ࡞͸͢Δʂ

  33. γʔϯΛબΜͩͷʹʜʜʂʁ ݪҼ γʔϯͷঢ়ଶΛอଘ ͍ͯ͠ͳ͍͔Β

  34. 4UFQ  γʔϯঢ়ଶͷอଘͳΒ ΦϨʹ೚ͤΖ

  35. /46TFS"DUJWJUZ w ΞϓϦͷঢ়ଶΛදݱ͢ΔΫϥε w .VMUJQMF8JOEPXTҎ֎Ͱ΋࢖ΘΕΔ  )BOEP⒎ 4JSJ4IPSUDVUT  w

    ΞϓϦݻ༗ͷঢ়ଶΛ֨ೲͰ͖ΔϓϩύςΟ͕͋Δ  ೖΕΒΕΔ΋ͷ /4"SSBZ /4%BUB /4%BUF /4%JDUJPOBSZ /4/VMM /4/VNCFS /44FU /44USJOH /463- var userInfo: [AnyHashable : Any]? { get set }
  36. /46TFS"DUJWJUZͷ࡞Γํ w ͔͋Β͡ΊܗࣜΛࣝผ͢ΔͨΊͷจࣈྻΛ *OGPQMJTUͰએݴ͓ͯ͘͠ w /46TFS"DUJWJUZͷΠχγϟϥΠβʹͦͷࣝผࢠ Λ౉ͯ͠ੜ੒͢Δ <key>NSUserActivityTypes</key> <array> <string>com.hironytic.Sessions.SessionDetail</string>

    </array> let userActivity = NSUserActivity(activityType: "com.hironytic.Sessions.SessionDetail") ˣ1SPQFSUZ-JTUදࣔ ˣ4PVSDF$PEFදࣔ
  37. 04͔ΒͷཁٻʹԠ͑Δ w 4DFOF%FMFHBUFͷϝιουʢࣗ෼Ͱ࣮૷͢Δʣ w Ҿ਺ʹ౉͞Εͨγʔϯʹର͢Δ/46TFS"DUJWJUZ Λฦ͢ͱɺ04͕ঢ়ଶΛهԱͯ͘͠ΕΔ func stateRestorationActivity(for scene: UIScene)

    -> NSUserActivity? { ... }
  38. ొ৔ਓ෺૬ؔਤ 6*"QQMJDBUJPO "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF 6*8JOEPX

    7JFX$POUSPMMFS 6*7JFX$POUSPMMFS 7JFX 6*8JOEPX4DFOF 6*4DFOF /4*UFN1SPWJEFS 6*%SBH*UFN 6*4DFOF4FTTJPO ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ ੵΉ /46TFS"DUJWJUZ
  39. ొ৔ਓ෺૬ؔਤ 6*"QQMJDBUJPO "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO 6*8JOEPX 7JFX$POUSPMMFS 6*7JFX$POUSPMMFS 7JFX 6*8JOEPX4DFOF

    6*4DFOF /4*UFN1SPWJEFS 6*%SBH*UFN 6*4DFOF4FTTJPO ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ ੵΉ /46TFS"DUJWJUZ 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF
  40. ొ৔ਓ෺૬ؔਤ 6*"QQMJDBUJPO "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO 6*8JOEPX 7JFX$POUSPMMFS 6*7JFX$POUSPMMFS 7JFX /4*UFN1SPWJEFS

    6*%SBH*UFN 6*4DFOF4FTTJPO ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ ੵΉ /46TFS"DUJWJUZ 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF 6*8JOEPX4DFOF 6*4DFOF
  41. ొ৔ਓ෺૬ؔਤ 6*"QQMJDBUJPO "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO 6*8JOEPX 7JFX$POUSPMMFS 6*7JFX$POUSPMMFS 7JFX /4*UFN1SPWJEFS

    6*%SBH*UFN 6*4DFOF4FTTJPO ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ ੵΉ /46TFS"DUJWJUZ 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF 6*8JOEPX4DFOF 6*4DFOF VTFS"DUJWJUZ
  42. ొ৔ਓ෺૬ؔਤ 6*"QQMJDBUJPO "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO /4*UFN1SPWJEFS 6*%SBH*UFN 6*4DFOF4FTTJPO ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ

    ੵΉ /46TFS"DUJWJUZ 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF 6*8JOEPX4DFOF 6*4DFOF VTFS"DUJWJUZ 6*8JOEPX 7JFX$POUSPMMFS 6*7JFX$POUSPMMFS 7JFX XJOEPX4DFOF XJOEPX WJFX
  43. 4DFOF%FMFHBUFͷϝιουΛ࣮૷ func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? { return scene.userActivity

    } 4DFOF%FMFHBUF
  44. 6*4DFOFʹ/46TFS"DUJWJUZΛηοτ override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let userActivity

    = NSUserActivity(activityType: "com.hironytic.Sessions.SessionDetail") userActivity.userInfo = [ "sessionId": sessionId ] view.window?.windowScene?.userActivity = userActivity } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) view.window?.windowScene?.userActivity = nil ... } 4FTTJPO%FUBJM7JFX$POUSPMMFSʢৄࡉը໘ʣ 4FTTJPO-JTU7JFX$POUSPMMFSʢҰཡը໘ʣ
  45. ঢ়ଶͷ෮ݩ w γʔϯʹ઀ଓ͢Δͱ͖ʹݺ͹ΕΔ4DFOF%FMFHBUF ͷTDFOF @XJMM$POOFDU5PPQUJPOT ͷୈ̎Ҿ਺ ʹ౉͞Εͨ6*4DFOF4FTTJPO͔Β /46TFS"DUJWJUZ͕औΕΔ w /46TFS"DUJWJUZʹ֮͑ͨ৘ใΛ΋ͱʹը໘ભҠ͢

    Δ
  46. ొ৔ਓ෺૬ؔਤ 6*"QQMJDBUJPO "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO 6*8JOEPX 7JFX$POUSPMMFS 6*7JFX$POUSPMMFS 7JFX 6*8JOEPX4DFOF

    6*4DFOF /46TFS"DUJWJUZ /4*UFN1SPWJEFS 6*%SBH*UFN 6*4DFOF4FTTJPO ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ ੵΉ 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF
  47. ొ৔ਓ෺૬ؔਤ 6*"QQMJDBUJPO "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO 6*8JOEPX 7JFX$POUSPMMFS 6*7JFX$POUSPMMFS 7JFX 6*8JOEPX4DFOF

    6*4DFOF /46TFS"DUJWJUZ /4*UFN1SPWJEFS 6*%SBH*UFN ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ ੵΉ 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF 6*4DFOF4FTTJPO
  48. ొ৔ਓ෺૬ؔਤ 6*"QQMJDBUJPO "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO 6*8JOEPX 7JFX$POUSPMMFS 6*7JFX$POUSPMMFS 7JFX 6*8JOEPX4DFOF

    6*4DFOF /4*UFN1SPWJEFS 6*%SBH*UFN ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ ੵΉ 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF 6*4DFOF4FTTJPO /46TFS"DUJWJUZ TUBUF3FTUPSBUJPO"DUJWJUZ
  49. ঢ়ଶͷ෮ݩ func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions:

    UIScene.ConnectionOptions) { ... if let activity = session.stateRestorationActivity { setupViewController(with: activity) } } func setupViewController(with activity: NSUserActivity) { guard activity.activityType == "com.hironytic.Sessions.SessionDetail" else { return } guard let sessionId = activity.userInfo?["sessionId"] as? String else { return } guard let navigationController = window?.rootViewController as? UINavigationController else { return } let detailVc = SessionDetailViewController.instantiate(sessionId: sessionId) navigationController.pushViewController(detailVc, animated: false) } 4DFOF%FMFHBUF
  50. γʔϯ͕෮ݩ͞ΕΔΑ͏ʹͳͬͨ

  51. ঢ়ଶͷอଘ͸γʔϯ"1*ͷػೳ w .VMUJQMF8JOEPXTʹରԠ͠ͳͯ͘΋࢖͑Δ w ࣮ࡍɺJ1IPOFͰ΋࢖͑Δ w ͨͩ͠ɺγʔϯ"1*͸J04͔Β

  52. 4UFQ  ϓϩάϥϜ͔Β ৽͍͠γʔϯΛల։ʂ

  53. ొ৔ਓ෺૬ؔਤ 6*"QQMJDBUJPO "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF 6*8JOEPX

    7JFX$POUSPMMFS 6*7JFX$POUSPMMFS 7JFX 6*8JOEPX4DFOF 6*4DFOF /46TFS"DUJWJUZ /4*UFN1SPWJEFS 6*4DFOF4FTTJPO ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ ੵΉ 6*%SBH*UFN
  54. ొ৔ਓ෺૬ؔਤ 6*"QQMJDBUJPO "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF 6*8JOEPX

    7JFX$POUSPMMFS 6*7JFX$POUSPMMFS 7JFX 6*8JOEPX4DFOF 6*4DFOF /46TFS"DUJWJUZ 6*4DFOF4FTTJPO ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ ੵΉ 6*%SBH*UFN /4*UFN1SPWJEFS
  55. ొ৔ਓ෺૬ؔਤ 6*"QQMJDBUJPO "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF 6*8JOEPX

    7JFX$POUSPMMFS 6*7JFX$POUSPMMFS 7JFX 6*8JOEPX4DFOF 6*4DFOF 6*4DFOF4FTTJPO ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ 6*%SBH*UFN /4*UFN1SPWJEFS /46TFS"DUJWJUZ /4*UFN1SPWJEFS8SJUJOH ੵΉ
  56. ৽͘͠Ͱ͖ͨγʔϯͰ /46TFS"DUJWJUZΛड͚औΔ w γʔϯʹ઀ଓ͢Δͱ͖ʹݺ͹ΕΔ4DFOF%FMFHBUF ͷTDFOF @XJMM$POOFDU5PPQUJPOT ͷୈ̏Ҿ਺ ʹ౉͞Εͨ6*4DFOF$POOFDUJPO0QUJPOT͔Β /46TFS"DUJWJUZ͕औΕΔ w

    /46TFS"DUJWJUZʹ֮͑ͨ৘ใΛ΋ͱʹը໘Λߏங ͢Δ
  57. υϥοάͷ։࢝ class SessionListViewController: UITableViewController { ... override func viewDidLoad() {

    super.viewDidLoad() tableView.dragDelegate = self ... } ... } extension SessionListViewController: UITableViewDragDelegate { func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] { let session = sessions[indexPath.row] let userActivity = NSUserActivity(activityType: "com.hironytic.Sessions.SessionDetail") userActivity.userInfo = [ "sessionId": session.id ] let itemProvider = NSItemProvider(object: userActivity) let dragItem = UIDragItem(itemProvider: itemProvider) return [dragItem] } } 4FTTJPO-JTU7JFX$POUSPMMFSʢҰཡը໘ʣ
  58. /46TFS"DUJWJUZͷड͚औΓ func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions:

    UIScene.ConnectionOptions) { ... if let activity = connectionOptions.userActivities.first ?? session.stateRestorationActivity { setupViewController(with: activity) } } 4DFOF%FMFHBUF
  59. υϥοάˍυϩοϓʹΑΔγʔϯͷ௥Ճ

  60. 4UFQ    ৽͍͠γʔϯ͸ ʮด͡Δʯʹ͍ͨ͠

  61. ৽͍͠γʔϯ͸ʮด͡Δʯʹ͍ͨ͠

  62. )*(ʹ΋ॻ͔Ε͍ͯΔ Use a Done or Close button in an auxiliary

    window. When a primary window displays a document, the window typically includes a Back button that lets people navigate to a parent view. In contrast, when an auxiliary window displays a document, the Back button should be replaced with a Done or Close button, because people expect to close an auxiliary window when they're finished working in it. IUUQTEFWFMPQFSBQQMFDPNEFTJHOIVNBOJOUFSGBDFHVJEFMJOFTJPTTZTUFNDBQBCJMJUJFTNVMUJQMFXJOEPXT
  63. /46TFS"DUJWJUZʹϑϥάΛ࣋ͨͤΔ extension SessionListViewController: UITableViewDragDelegate { func tableView(_ tableView: UITableView, itemsForBeginning

    session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] { let session = sessions[indexPath.row] let userActivity = NSUserActivity(activityType: "com.hironytic.Sessions.SessionDetail") userActivity.userInfo = [ "sessionId": session.id, "isAuxiliary": true, ] let itemProvider = NSItemProvider(object: userActivity) let dragItem = UIDragItem(itemProvider: itemProvider) return [dragItem] } } 4FTTJPO-JTU7JFX$POUSPMMFSʢҰཡը໘ʣ
  64. ৄࡉը໘ͷ7JFX$POUSPMMFS·ͰҾ͖౉͢ func setupViewController(with activity: NSUserActivity) { guard activity.activityType == "com.hironytic.Sessions.SessionDetail"

    else { return } guard let sessionId = activity.userInfo?["sessionId"] as? String else { return } guard let navigationController = window?.rootViewController as? UINavigationController else { return } let isAuxiliary = activity.userInfo?["isAuxiliary"] as? Bool ?? false let detailVc = SessionDetailViewController.instantiate(sessionId: sessionId, isAuxiliary: isAuxiliary) navigationController.pushViewController(detailVc, animated: false) } 4DFOF%FMFHBUF
  65. ϑϥάΛݟͯࠨ্ͷϘλϯΛมߋ override func viewDidLoad() { super.viewDidLoad() if isAuxiliary { let

    closeItem = UIBarButtonItem(title: "Close", style: .plain, target: self, action: #selector(closeButtonDidTap(_:))) navigationItem.leftBarButtonItem = closeItem } ... } 4FTTJPO%FUBJM7JFX$POUSPMMFSʢৄࡉը໘ʣ override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let userActivity = NSUserActivity(activityType: "com.hironytic.Sessions.SessionDetail") userActivity.userInfo = [ "sessionId": sessionId, "isAuxiliary": isAuxiliary, ] view.window?.windowScene?.userActivity = userActivity } ঢ়ଶอଘͷํʹ΋ ϑϥάΛ௥Ճɹˠ
  66. $MPTFϘλϯͷॲཧ @objc private func closeButtonDidTap(_ sender: Any) { guard let

    scene = view.window?.windowScene else { return } let options = UIWindowSceneDestructionRequestOptions() options.windowDismissalAnimation = .standard UIApplication.shared .requestSceneSessionDestruction(scene.session, options: options) } 4FTTJPO%FUBJM7JFX$POUSPMMFSʢৄࡉը໘ʣ
  67. ิॿతͳ΢Οϯυ΢ͱͯ͠ৼΔ෣͏Α͏ʹͳͬͨ

  68. ͦͷଞͷτϐοΫ

  69. γʔϯͷϥΠϑαΠΫϧ

  70. γʔϯͷϥΠϑαΠΫϧ IUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOVJLJUBQQ@BOE@FOWJSPONFOUNBOBHJOH@ZPVS@BQQ@T@MJGF@DZDMF TDFOF @XJMM$POOFDU5PPQUJPOT TDFOF8JMM&OUFS'PSFHSPVOE @ TDFOF%JE#FDPNF"DUJWF @ TDFOF8JMM3FTJHO"DUJWF @

    TDFOF%JE&OUFS#BDLHSPVOE @ TDFOF%JE%JTDPOOFDU @ Discarded γʔϯ͕ด͡ΒΕͨͱ͖ BQQMJDBUJPO @EJE%JTDBSE4DFOF4FTTJPOT "QQεΠονϟʔ͔Βดͨ͡ͱ͖͸ ʮดͨ͡΢Οϯυ΢Λ࠶ͼ։͘ʯ͕ ༗ޮͳ࣌఺Ͱ͸·ͩݺ͹Εͳ͍
  71. ొ৔ਓ෺૬ؔਤ "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF 6*8JOEPX 7JFX$POUSPMMFS

    6*7JFX$POUSPMMFS 7JFX 6*8JOEPX4DFOF 6*4DFOF /46TFS"DUJWJUZ /4*UFN1SPWJEFS 6*%SBH*UFN 6*4DFOF4FTTJPO ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ ੵΉ 6*"QQMJDBUJPO
  72. ొ৔ਓ෺૬ؔਤ "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF 6*8JOEPX 7JFX$POUSPMMFS

    6*7JFX$POUSPMMFS 7JFX /46TFS"DUJWJUZ /4*UFN1SPWJEFS 6*%SBH*UFN 6*4DFOF4FTTJPO ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ ੵΉ 6*"QQMJDBUJPO 6*8JOEPX4DFOF 6*4DFOF DPOOFDUFE4DFOFT
  73. ొ৔ਓ෺૬ؔਤ "QQ%FMFHBUF 6*"QQMJDBUJPO%FMFHBUF 6*4DFOF$POpHVSBUJPO 4DFOF%FMFHBUF 6*8JOEPX4DFOF%FMFHBUF  6*4DFOF%FMFHBUF 6*8JOEPX 7JFX$POUSPMMFS

    6*7JFX$POUSPMMFS 7JFX 6*8JOEPX4DFOF 6*4DFOF /46TFS"DUJWJUZ /4*UFN1SPWJEFS 6*%SBH*UFN ੜ੒ͯ͠ฦ͢ ΫϥεΛࢦఆ ੵΉ 6*"QQMJDBUJPO 6*4DFOF4FTTJPO PQFO4FTTJPOT
  74. γϯΫϩφΠζυγʔϯ ʬঢ়ଶͷಉظʭ

  75. γϯΫϩφΠζυγʔϯʬঢ়ଶͷಉظʭ w ಉ͡ΞϓϦͷγʔϯ Λ4QMJU7JFXͰಉ࣌ ʹදࣔ w Ұํͷૢ࡞Ͱɺ΋͏ Ұํͷը໘΋ߋ৽͞ Εͯ΄͍͠

  76. γʔϯؒͷঢ়ଶͷಉظํ๏ w ಛผͳํ๏͕͋ΔΘ͚Ͱ͸ͳ͍ ‎,70ɺ/PUJpDBUJPO$FOUFSɺ$PNCJOFɺଞʜ ͕Μ͹Ε͹͍͍͚ͩ w ϞσϧΛܦ༝͢ΔσʔλͷྲྀΕ͕୯ํ޲ʹͳͬͯ ͍Δͱಉظ͠΍͍͢

  77. ୯ํ޲σʔλϑϩʔͷྫʢ.77.ʣ 7JFX 7JFX.PEFM .PEFM

  78. ୯ํ޲σʔλϑϩʔͷྫʢ.77.ʣ 7JFX 7JFX.PEFM .PEFM 7JFX 7JFX.PEFM

  79. ୯ํ޲σʔλϑϩʔͷྫʢ'MVYʣ 7JFX "DUJPO 4UPSF 7JFX %JTQBUDIFS

  80. ޷͖ͳγʔϯΛબΜͰͶ

  81. ޷͖ͳγʔϯΛબΜͰͶ w ௨஌ɺ2VJDL"DUJPOTɺ4IPSUDVUͳͲ͔ΒΞϓϦΛݺͼ ग़͢ͱɺͲͷγʔϯ͕ΞΫςΟϒʹͳΔʁ w ΞϓϦΛݺͼग़͢ஈ֊Ͱ͸ɺ·ͩΞϓϦͷϓϩηε͸ى ಈ͍ͯ͠ͳ͍͔΋͠Εͳ͍ w ʮ5BSHFU$POUFOU*EFOUJpFSʯͱ ʮ"DUJWBUJPO$POEJUJPOTʯ

    Λ࢖ͬͯΞϓϦΛىಈͤͣʹγʔϯΛܾఆ͢Δ
  82. 5BSHFU$POUFOU*EFOUJpFS w ಺༰Λࣝผ͢ΔͨΊͷ೚ҙͷจࣈྻ w ͜ͷจࣈྻΛΩοΫ͢Δଆʹηοτ͢Δ  1VTI௨஌ʢ"1/4UBSHFUDPOUFOUJEΩʔʣ  6*"QQMJDBUJPO4IPSUDVU*UFNUBSHFU$POUFOU*EFOUJpFS 

    /46TFS"DUJWJUZUBSHFU$POUFOU*EFOUJpFS
  83. "DUJWBUJPO$POEJUJPOT w 6*4DFOFͷϓϩύςΟ BDUJWBUJPO$POEJUJPOT  w /41SFEJDBUFͷϓϩύςΟΛͭ࣋ͭ  DBO"DUJWBUF'PS5BSHFU$POUFOU*EFOUJpFS1SFEJDBUF 

    QSFGFST5P"DUJWBUF'PS5BSHFU$POUFOU*EFOUJpFS1SFEJDBUF
  84. "DUJWBUJPO$POEJUJPOT w 6*4DFOFͷϓϩύςΟ BDUJWBUJPO$POEJUJPOT  w /41SFEJDBUFͷϓϩύςΟΛͭ࣋ͭ  DBO"DUJWBUF'PS5BSHFU$POUFOU*EFOUJpFS1SFEJDBUF 

    QSFGFST5P"DUJWBUF'PS5BSHFU$POUFOU*EFOUJpFS1SFEJDBUF activationConditions.prefersToActivateForTargetContentIdentifierPredicate = NSPredicate(format: "self == %@", "com.hironytic.Sessions.SessionDetail/\(sessionId)")
  85. "DUJWBUJPO$POEJUJPOT w DBOͷํ͸ʮѻ͑Δ͔Ͳ͏͔ʯ QSFGFST5Pͷํ͸ʮΑΓ;͞Θ͍͔͠Ͳ͏͔ʯ w /41SFEJDBUFͷ੍ݶ  #MPDLΛ࢖͏ͷ͸μϝ  ਖ਼نදݱΛ൐͏΋ͷ͸μϝ

     TFMGҎ֎͸μϝ
  86. 3FDBQ

  87. 3FDBQ w .VMUJQMF8JOEPXTͱ͸Ͳ͏͍͏΋ͷ͔  ඪ४ʮϝϞʯΞϓϦΛྫʹͯ͠ղઆ w .VMUJQMF8JOEPXT΁ͷରԠํ๏  γʔϯ"1*ʹରԠͯ͠&OBCMFʹ͢Δ 

    γʔϯͷঢ়ଶอଘ  υϥοάʹΑΔ৽͍͠γʔϯͷ࡞੒ w ͦͷଞͷτϐοΫ  ϥΠϑαΠΫϧ  ঢ়ଶͷಉظ  ద੾ͳγʔϯͷબ୒
  88. ͓·͚ɿ"QQMFެࣜࢿྉ w 88%$*OUSPEVDJOH.VMUJQMF8JOEPXTPOJ1BE IUUQTEFWFMPQFSBQQMFDPNXXED w 88%$"SDIJUFDUJOH:PVS"QQGPS.VMUJQMF8JOEPXT IUUQTEFWFMPQFSBQQMFDPNXXED w 88%$5BSHFUJOH$POUFOUXJUI.VMUJQMF8JOEPXT IUUQTEFWFMPQFSBQQMFDPNXXED

    w %PDVNFOUBUJPO6*,JU"QQBOE&OWJSPONFOU4DFOFT IUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOVJLJUBQQ@BOE@FOWJSPONFOUTDFOFT w )VNBO*OUFSGBDF(VJEFMJOF.VMUJQMF8JOEPXTPOJ1BE IUUQTEFWFMPQFSBQQMFDPNEFTJHOIVNBOJOUFSGBDFHVJEFMJOFTJPTTZTUFNDBQBCJMJUJFTNVMUJQMF XJOEPXT w 4VQQPSUJOH.VMUJQMF8JOEPXTPOJ1BEʢαϯϓϧίʔυʣ IUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOVJLJUBQQ@BOE@FOWJSPONFOUTDFOFT TVQQPSUJOH@NVMUJQMF@XJOEPXT@PO@JQBE
  89. ͓·͚ɿ9DPEFͰ৽نϓϩδΣΫτ࡞੒௚ޙ ʹɺJ04Λαϙʔτ͢Δํ๏ w %FQMPZNFOU5BSHFUΛมߋ͢Δ w 4DFOF%FMFHBUFʹJ04Ҏ߱ͷଐੑΛ͚ͭΔ w "QQ%FMFHBUFʹXJOEPXϓϩύςΟΛੜ΍͢ w "QQ%FMFHBUFͷγʔϯབྷΈͷϝιουʹJ04Ҏ߱ͷଐੑΛͭ

    ͚Δ @available(iOS 13.0, *) class SceneDelegate: UIResponder, UIWindowSceneDelegate { ... @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? ... @available(iOS 13.0, *) func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {...} @available(iOS 13.0, *) func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {...}
  90. ͓·͚̏ɿγʔϯͷλΠτϧ 6*4DFOFͷUJUMFϓϩύςΟͷจࣈྻ͕ "QQεΠονϟʔ΍"QQ&YQPTÉʹදࣔ͞ΕΔ

  91. ͓·͚ɿ4XJGU6* w 9DPEF͕࡞ΔςϯϓϨʔτʢJ04޲͚ʣ  4DFOF%FMFHBUFͷɹTDFOF XJMM$POOFDU5PPQUJPOT Ͱ 6*)PTUJOH$POUSPMMFSΛ࡞ͬͯ4XJGU6*Λද͍ࣔͯ͠Δ w J04Ͱ͸

     4XJGU6*ͷΈͰγʔϯ͕αϙʔτ͞ΕΔ  88%$"QQFTTFOUJBMTJO4XJGU6* IUUQTEFWFMPQFSBQQMFDPNXXED