$30 off During Our Annual Pro Sale. View Details »

iPadOSDC: Multiple Windows

Hiron
September 20, 2020

iPadOSDC: Multiple Windows

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

Hiron

September 20, 2020
Tweet

More Decks by Hiron

Other Decks in Programming

Transcript

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

    View Slide

  2. J1BE04
    5JNF

    "QSJM

    +VOF

    J1IPOF04
    J1IPOF04
    J04
    J04
    J1BE04
    J1BE
    .VMUJUBTLJOH
    .VMUJQMF
    8JOEPXT

    View Slide

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

    View Slide

  4. J1BE04
    w J1BEઐ༻ͷ04
    w جຊతʹJ04ͱҰॹʹόʔδϣϯΞοϓ͍ͯ͠Δ
    w ։ൃ͸J044%,Ͱ
    w ΞϓϦͷόΠφϦ΋J04J1BE04ڞ௨
    w ։ൃऀ͔ΒݟΕ͹J04ͱͷڥք͸͋·Γͳ͍

    View Slide

  5. .VMUJQMF8JOEPXT
    ʜͳʹͦΕ͓͍͍͠ͷʁ

    View Slide

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

    View Slide

  7. View Slide

  8. ʮϝϞʯͷಈ࡞

    w ΞϓϦΞΠίϯͷϝχϡʔʮ͢΂ͯͷ΢Οϯυ΢Λදࣔʯ
    ʢ"QQ&YQPTÉʣ
    w "QQ&YQPTÉͷӈ্ʹ͋ΔʴϘλϯ͔Β৽͍͠΢Οϯυ΢
    Λ։͘͜ͱ͕Ͱ͖Δ
    w ৽͍͠΢Οϯυ΢͸"QQεΠονϟʔʹฒΜͰɺΞϓϦΛ੾
    Γସ͑Δײ֮Ͱ΢Οϯυ΢Λ੾Γସ͑ΒΕΔ
    w 4QMJU7JFXͰಉ͡ΞϓϦͷผ΢Οϯυ΢Λฒ΂ΒΕΔ

    View Slide

  9. View Slide

  10. ʮϝϞʯͷಈ࡞

    w ϝϞͷΞΠςϜΛυϥοά͢Δ͜ͱͰผͷ΢Οϯυ΢Λ։
    ͘͜ͱ͕Ͱ͖Δ
    w ը໘ͷࠨӈͷ୺ʹυϥοά͢Δͱ4QMJU7JFXͰɺ্୺ʹ
    υϥοά͢Δͱશը໘Ͱɺ৽͍͠΢Οϯυ΢͕։͔ΕΔ
    w ϝϞΞΠςϜ͔Β։͍ͨ΢Οϯυ΢͸ɺϝϞͷҰཡʹ໭Δ
    ͜ͱ͸ͳ͘ɺฤू͕ऴΘͬͨΒด͡Δ6*ʹͳ͍ͬͯΔ

    View Slide

  11. View Slide

  12. ʮϝϞʯͷಈ࡞

    w "QQεΠονϟʔ΍ɺ"QQ&YQPTÉͰ΢Οϯυ
    ΢Λ্ํ޲ʹεϫΠϓ͢Δͱɺ΢Οϯυ΢Λด͡
    Δ͜ͱʹͳΔ
    ʢ͜Ε·ͰͷΞϓϦΛऴྃͤ͞Δͷͱಉ͡ૢ࡞ʣ
    w "QQ&YQPTÉʹดͨ͡΢Οϯυ΢Λ࠶ͼ։͘Ϙλ
    ϯ͕දࣔ͞ΕΔ

    View Slide

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

    View Slide

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

    View Slide

  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
    ੜ੒ͯ͠ฦ͢
    ΫϥεΛࢦఆ
    ੵΉ

    View Slide

  16. ʰ4FTTJPOTʱ
    w Ұཡը໘ͱৄࡉը໘͔ΒͳΔ
    γϯϓϧͳΞϓϦ
    w ৄࡉը໘Ͱελʔͷ෇͚֎͠
    ͕Ͱ͖Δ
    w .VMUJQMF8JOEPXTʹະର
    ԠˠରԠ͍ͤͯ͘͞
    IUUQTHJUIVCDPNIJSPOZUJD.VMUJQMF8JOEPXT4BNQMF

    View Slide

  17. 4UFQ

    γʔϯ͸؆୯
    ͦ͏ɺJ04Ҏ߱ͷΈͳΒͶ

    View Slide

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

    View Slide

  19. *OGPQMJTUʹ4DFOF.BOJGFTUΛ௥Ճ
    UIApplicationSceneManifest

    UIApplicationSupportsMultipleScenes

    UISceneConfigurations

    UIWindowSceneSessionRoleApplication


    UISceneConfigurationName
    Default Configuration
    UISceneDelegateClassName
    $(PRODUCT_MODULE_NAME).SceneDelegate
    UISceneStoryboardFile
    Main




    ˡ1SPQFSUZ-JTUදࣔ
    4PVSDF$PEFදࣔˠ

    View Slide

  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)
    }
    ...
    }

    View Slide

  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) {
    ...

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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Ͱ͸ݺ͹ΕΔ

    View Slide

  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 }
    }

    View Slide

  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?

    View Slide

  30. 4UFQ

    ෳ਺ͷγʔϯΛαϙʔτʂ
    ઓ͍ͷ࢝·Γ

    View Slide

  31. *OGPQMJTUͷ4DFOF.BOJGFTUΛมߋ
    UIApplicationSceneManifest

    UIApplicationSupportsMultipleScenes

    UISceneConfigurations

    UIWindowSceneSessionRoleApplication


    UISceneConfigurationName
    Default Configuration
    UISceneDelegateClassName
    $(PRODUCT_MODULE_NAME).SceneDelegate
    UISceneStoryboardFile
    Main




    ˡ1SPQFSUZ-JTUදࣔ
    4PVSDF$PEFදࣔˠ

    View Slide

  32. ͜Ε͚ͩͰ࠷௿ݶͷಈ࡞͸͢Δʂ

    View Slide

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

    View Slide

  34. 4UFQ

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

    View Slide

  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 }

    View Slide

  36. /46TFS"DUJWJUZͷ࡞Γํ
    w ͔͋Β͡ΊܗࣜΛࣝผ͢ΔͨΊͷจࣈྻΛ
    *OGPQMJTUͰએݴ͓ͯ͘͠
    w /46TFS"DUJWJUZͷΠχγϟϥΠβʹͦͷࣝผࢠ
    Λ౉ͯ͠ੜ੒͢Δ
    NSUserActivityTypes

    com.hironytic.Sessions.SessionDetail

    let userActivity = NSUserActivity(activityType: "com.hironytic.Sessions.SessionDetail")
    ˣ1SPQFSUZ-JTUදࣔ
    ˣ4PVSDF$PEFදࣔ

    View Slide

  37. 04͔ΒͷཁٻʹԠ͑Δ
    w 4DFOF%FMFHBUFͷϝιουʢࣗ෼Ͱ࣮૷͢Δʣ
    w Ҿ਺ʹ౉͞Εͨγʔϯʹର͢Δ/46TFS"DUJWJUZ
    Λฦ͢ͱɺ04͕ঢ়ଶΛهԱͯ͘͠ΕΔ
    func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? {
    ...
    }

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  43. 4DFOF%FMFHBUFͷϝιουΛ࣮૷
    func stateRestorationActivity(for scene: UIScene)
    -> NSUserActivity? {
    return scene.userActivity
    }
    4DFOF%FMFHBUF

    View Slide

  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ʢҰཡը໘ʣ

    View Slide

  45. ঢ়ଶͷ෮ݩ
    w γʔϯʹ઀ଓ͢Δͱ͖ʹݺ͹ΕΔ4DFOF%FMFHBUF
    ͷTDFOF @XJMM$POOFDU5PPQUJPOT
    ͷୈ̎Ҿ਺
    ʹ౉͞Εͨ6*4DFOF4FTTJPO͔Β
    /46TFS"DUJWJUZ͕औΕΔ
    w /46TFS"DUJWJUZʹ֮͑ͨ৘ใΛ΋ͱʹը໘ભҠ͢
    Δ

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  50. γʔϯ͕෮ݩ͞ΕΔΑ͏ʹͳͬͨ

    View Slide

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

    View Slide

  52. 4UFQ

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    ੵΉ

    View Slide

  56. ৽͘͠Ͱ͖ͨγʔϯͰ
    /46TFS"DUJWJUZΛड͚औΔ
    w γʔϯʹ઀ଓ͢Δͱ͖ʹݺ͹ΕΔ4DFOF%FMFHBUF
    ͷTDFOF @XJMM$POOFDU5PPQUJPOT
    ͷୈ̏Ҿ਺
    ʹ౉͞Εͨ6*4DFOF$POOFDUJPO0QUJPOT͔Β
    /46TFS"DUJWJUZ͕औΕΔ
    w /46TFS"DUJWJUZʹ֮͑ͨ৘ใΛ΋ͱʹը໘Λߏங
    ͢Δ

    View Slide

  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ʢҰཡը໘ʣ

    View Slide

  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

    View Slide

  59. υϥοάˍυϩοϓʹΑΔγʔϯͷ௥Ճ

    View Slide

  60. 4UFQ


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

    View Slide

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

    View Slide

  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

    View Slide

  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ʢҰཡը໘ʣ

    View Slide

  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

    View Slide

  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
    }
    ঢ়ଶอଘͷํʹ΋
    ϑϥάΛ௥Ճɹˠ

    View Slide

  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ʢৄࡉը໘ʣ

    View Slide

  67. ิॿతͳ΢Οϯυ΢ͱͯ͠ৼΔ෣͏Α͏ʹͳͬͨ

    View Slide

  68. ͦͷଞͷτϐοΫ

    View Slide

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

    View Slide

  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εΠονϟʔ͔Βดͨ͡ͱ͖͸
    ʮดͨ͡΢Οϯυ΢Λ࠶ͼ։͘ʯ͕
    ༗ޮͳ࣌఺Ͱ͸·ͩݺ͹Εͳ͍

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  74. γϯΫϩφΠζυγʔϯ
    ʬঢ়ଶͷಉظʭ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  82. 5BSHFU$POUFOU*EFOUJpFS
    w ಺༰Λࣝผ͢ΔͨΊͷ೚ҙͷจࣈྻ
    w ͜ͷจࣈྻΛΩοΫ͢Δଆʹηοτ͢Δ
    1VTI௨஌ʢ"1/4UBSHFUDPOUFOUJEΩʔʣ
    6*"QQMJDBUJPO4IPSUDVU*UFNUBSHFU$POUFOU*EFOUJpFS
    /46TFS"DUJWJUZUBSHFU$POUFOU*EFOUJpFS

    View Slide

  83. "DUJWBUJPO$POEJUJPOT
    w 6*4DFOFͷϓϩύςΟ BDUJWBUJPO$POEJUJPOT

    w /41SFEJDBUFͷϓϩύςΟΛͭ࣋ͭ
    DBO"DUJWBUF'PS5BSHFU$POUFOU*EFOUJpFS1SFEJDBUF
    QSFGFST5P"DUJWBUF'PS5BSHFU$POUFOU*EFOUJpFS1SFEJDBUF

    View Slide

  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)")

    View Slide

  85. "DUJWBUJPO$POEJUJPOT
    w DBOͷํ͸ʮѻ͑Δ͔Ͳ͏͔ʯ
    QSFGFST5Pͷํ͸ʮΑΓ;͞Θ͍͔͠Ͳ͏͔ʯ
    w /41SFEJDBUFͷ੍ݶ
    #MPDLΛ࢖͏ͷ͸μϝ
    ਖ਼نදݱΛ൐͏΋ͷ͸μϝ
    TFMGҎ֎͸μϝ

    View Slide

  86. 3FDBQ

    View Slide

  87. 3FDBQ
    w .VMUJQMF8JOEPXTͱ͸Ͳ͏͍͏΋ͷ͔
    ඪ४ʮϝϞʯΞϓϦΛྫʹͯ͠ղઆ
    w .VMUJQMF8JOEPXT΁ͷରԠํ๏
    γʔϯ"1*ʹରԠͯ͠&OBCMFʹ͢Δ
    γʔϯͷঢ়ଶอଘ
    υϥοάʹΑΔ৽͍͠γʔϯͷ࡞੒
    w ͦͷଞͷτϐοΫ
    ϥΠϑαΠΫϧ
    ঢ়ଶͷಉظ
    ద੾ͳγʔϯͷબ୒

    View Slide

  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

    View Slide

  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) {...}

    View Slide

  90. ͓·͚̏ɿγʔϯͷλΠτϧ
    6*4DFOFͷUJUMFϓϩύςΟͷจࣈྻ͕
    "QQεΠονϟʔ΍"QQ&YQPTÉʹදࣔ͞ΕΔ

    View Slide

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

    View Slide