Upgrade to Pro — share decks privately, control downloads, hide ads and more …

iOSには無いmacOS独自機能をCatalystで実装する #iosdc #d/make_m...

fromkk
September 20, 2020

iOSには無いmacOS独自機能をCatalystで実装する #iosdc #d/make_macos_apps_with_catalyst

これまではmacOSのアプリケーションを開発しようと思うとAppKitというUIKitとは異なるframeworkを利用する必要がありiOSアプリの開発とは勝手が異なりました。​
そんな中WWDC 2018でmacOS Mojaveの一部のアプリケーションにUIKitをmacOSに対応したアプリケーションが搭載されることが発表されました。
さらに、翌年のWWDC 2019にてその機能が開発者向けに「Project Catalyst」として公開されました。
Catalyst対応はXcodeでチェックを一つ入れるだけで簡単にビルドをすることが可能です。
しかし、macOSらしいアプリケーションを作ろうと思うとすべきことが色々とあります。
WWDCでもサイドバーのUIをmacOSらしくする方法などが紹介されていましたが、WWDCで紹介されなかった機能がいくつもあります。
CatalystにおいてiOSには無い機能として「メニュー」、「Touch Bar」、「Toolbar」があげられます。
本トークではCatalystに対応した2つのアプリのリリース経験とCatalystに関する本を執筆した内容を基に、
対応方法の基礎から、先述したメニューやTouch Bar、Toolbarへの対応方法をご紹介します。

fromkk

September 20, 2020
Tweet

More Decks by fromkk

Other Decks in Programming

Transcript

  1. 1SPpMF struct Profile { let name = "Kazuya Ueoka" let

    company = "note, Inc." let twitter = "@fromkk" let github = "fromkk" let qiita = "fromkk" let note = "fromkk" } • 2
  2. 3

  3. 10

  4. 14

  5. w "QQ%FMFHBUF 6*3FTQPOEFS ʹCVJME.FOVͱ͍͏ϝιου͕ ͍ΔͷͰ࣮૷ΛՃ͑Δ w CVJMEFSTZTUFNʹDPOUFYUͷछྨ͕౉͞ΕΔͷͰϋϯυϦϯά ͢Δ ࣮૷ํ๏ #if

    targetEnvironment(macCatalyst) override func buildMenu(with builder: UIMenuBuilder) { super.buildMenu(with: builder) // TODO: build your menu } #endif IUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOVJLJUVJSFTQPOEFSCVJMENFOV 24
  6. 6*.FOV w ϝχϡʔΛߏ੒͢ΔΫϥε w λΠτϧ΍ը૾Λઃఆ͢Δ͜ͱ͕Ͱ͖Δ w DIJMESFOʹ͸6*.FOV&MFNFOUΛܧঝ͍ͯ͠ΔΫϥεΛೖΕΔ͜ͱ͕Ͱ͖ΔͷͰ࠶ؼ తʹϝχϡʔΛ࡞Δ͜ͱ͕Մೳ w ϝχϡʔͷ߲໨͕ͭ6*.FOVɺ಺෦͸6*.FOV&MFNFOUͷDIJMESFO

    w BDUJPO4FMFDUPS͸࣮ߦՄೳͳ!PCKDͳϝιουΛࢦఆՄೳ IUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOVJLJUVJNFOV init(title: String, image: UIImage? = nil, identifier: UIMenu.Identifier? = nil, options: UIMenu.Options = [], children: [UIMenuElement] = []) 26
  7. 6*.FOV#VJMEFS w ϝχϡʔͷߏ੒Λ؅ཧ͢ΔΫ ϥε w ϝχϡʔͷ௥Ճɺ࡟আɺೖΕ ସ͕͑Մೳ IUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOVJLJUVJNFOVCVJMEFS /// Replace

    an identified menu with a menu. /// /// @param replacedIdentifier The identifier of the menu to be replaced. /// @param replacementGroup The replacement menu. func replace(menu replacedIdentifier: UIMenu.Identifier, with replacementMenu: UIMenu) /// Replace the children of an identified parent menu. /// /// @param parentIdentifier The identifier of the parent menu. /// @param childrenBlock A block that returns the new children, given the old children. func replaceChildren(ofMenu parentIdentifier: UIMenu.Identifier, from childrenBlock: ([UIMenuElement]) -> [UIMenuElement]) /// Insert a sibling menu before an identified sibling menu. /// /// @param siblingGroup The sibling menu to insert. /// @param siblingIdentifier The identifier of the sibling menu to insert before. func insertSibling(_ siblingMenu: UIMenu, beforeMenu siblingIdentifier: UIMenu.Identifier) /// Insert a sibling menu before an identified sibling menu. /// /// @param siblingGroup The sibling menu to insert. /// @param siblingIdentifier The identifier of the sibling menu to insert before. func insertSibling(_ siblingMenu: UIMenu, afterMenu siblingIdentifier: UIMenu.Identifier) /// Insert a child menu at the start of an identified parent menu. /// /// @param childGroup The child menu to insert. /// @param parentIdentifier The identifier of the parent menu to insert at the start of. func insertChild(_ childMenu: UIMenu, atStartOfMenu parentIdentifier: UIMenu.Identifier) /// Insert a child menu at the end of an identified parent menu. /// /// @param childGroup The child menu to insert. /// @param parentIdentifier The identifier of the parent menu to insert at the end of. func insertChild(_ childMenu: UIMenu, atEndOfMenu parentIdentifier: UIMenu.Identifier) /// Remove an identified menu. /// /// @param removedIdentifier The menu to remove. func remove(menu removedIdentifier: UIMenu.Identifier) 27
  8. 6*"DUJPO w Ϋϩʔδϟʔ CMPDLT Λݺͼग़ͤΔ w 6*.FOV&MFNFOUΛܧঝ͍ͯ͠ΔͷͰ6*.FOVͷDIJMESFOʹ௥ Ճ͢Δ͜ͱ͕Ͱ͖Δ 28 IUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOVJLJUVJBDUJPO

    public convenience init(title: String, image: UIImage? = nil, identifier: UIAction.Identifier? = nil, discoverabilityTitle: String? = nil, attributes: UIMenuElement.Attributes = [], state: UIMenuElement.State = .off, handler: @escaping UIActionHandler)
  9. 6*$PNNBOE w ηϨΫλʔΛݺͼग़ͤΔ w 6*.FOV&MFNFOUΛܧঝ͍ͯ͠ΔͷͰ6*.FOVͷDIJMESFOʹ௥ Ճ͢Δ͜ͱ͕Ͱ͖Δ 29 IUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOVJLJUVJDPNNBOE public convenience

    init(title: String, image: UIImage? = nil, action: Selector, propertyList: Any? = nil, alternates: [UICommandAlternate] = [], discoverabilityTitle: String? = nil, attributes: UIMenuElement.Attributes = [], state: UIMenuElement.State = .off)
  10. ࣮૷ํ๏ • NSTouchBar.CustomizationIdentifierΛఆٛ͢Δ • Touch BarͷछྨΛࣝผ͢Δ 36 extension NSTouchBar.CustomizationIdentifier {

    static let myTouchBar = NSTouchBar.CustomizationIdentifier( "me.fromkk.CatalystSampler.MyTouchBar" ) }
  11. ࣮૷ํ๏ 37 extension NSTouchBarItem.Identifier { static let prev = NSTouchBarItem.Identifier(

    "me.fromkk.CatalystSampler.prev" ) static let next = NSTouchBarItem.Identifier( "me.fromkk.CatalystSampler.next" ) } • NSTouchBarItem.IdentifierΛఆٛ͢Δ • Touch Barʹදࣔ͢Δ߲໨ͷछྨΛࣝผ͢Δ
  12. ࣮૷ํ๏ w $BUBMZTUͷ৔߹ʹ6*3FTQPOEFSʹ૿͍͑ͯΔNBLF5PVDI#BSϝιουΛ࣮૷͢Δ 38 #if targetEnvironment(macCatalyst) override func makeTouchBar() ->

    NSTouchBar? { let touchBar = NSTouchBar() touchBar.customizationIdentifier = .myTouchBar touchBar.defaultItemIdentifiers = [.prev, .next, .otherItemsProxy] touchBar.customizationAllowedItemIdentifiers = [.prev, .next] touchBar.delegate = self return touchBar } #endif
  13. NSTouchBarͷϓϩύςΟ • customizationIdentifier:Touch BarΛࣝผ͢ΔͨΊͷ஋ • customizationAllowedItemIdentifiers:ΧελϚΠζΛڐՄ͢Δࣝผࢠ • customizationRequiredItemIdentifiers:ΧελϚΠζ࣌ʹ࡟আͰ͖ͳ͍ࣝผࢠ • defaultItemIdentifiers:ΧελϚΠζ͢Δલʹදࣔ͢Δࣝผࢠ

    • itemIdentifiers:ొ࿥ࡁΈͷࣝผࢠ • principalItemIdentifier:தԝʹදࣔ͞ΕΔॏཁͳࣝผࢠ • escapeKeyReplacementItemIdentifier:Esc ΩʔΛࠩ͠ସ͑Δ৔߹ͷࣝผࢠ • templateItems:ࣝผࢠ͔ΒΠϯελϯεԽ͞Ε߲ͨ໨Λอ࣋͢ΔϓϩύςΟ • delegate: ࣝผࢠ͔ΒΠϯελϯεΛੜ੒͢ΔॲཧΛҠৡ 39
  14. ࣮૷ํ๏ • NSTouchBarDelegateΛ࣮૷͢Δ • func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier:

    NSTouchBarItem.Identifier) -> NSTouchBarItem? • identifierʹࣄલʹఆٛͨ͠஋͕౉͞ΕΔͷͰNSTouchBarItem ͷαϒΫϥεΛੜ੒ͯ͠ฦ͢ • NSTouchBarItemʹ͸customizationLabelΛઃఆ͓ͯ͘͠ͱΧ ελϚΠζ࣌ʹϥϕϧ͕දࣔ͞ΕΔ 41
  15. ݕࡧॱং IUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOBQQLJUOTUPVDICBS 10. App delegate 9. App object 8. Main

    window’s controller 7. Main window’s delegate 6. Main window 5. Main window’s first responder 4. Key window’s controller 3. Key window’s delegate 2. Key window 1. Key window’s first responder 5PVDI#BSΛݕࡧ͢ΔॱংʢԼ͔Β্ʹʣ 6*3FTQPOEFSͷαϒΫϥεΛൃݟ͢Δͱߋʹݕࡧ 9. App delegate 8. App object 7. Key window’s controller 6. Key window 5. View controller that is closest to root of window 4. View that is closest to root of window 3. Intermediate view controllers and views 2. View controller of key window’s first responder 1. Key window’s first responder 42
  16. ஫ҙ఺ w 6*3FTQPOEFSΛܧঝͯ͠Ε͹දࣔ͞ΕΔ͔ͱࢥ͕ͬͨ4XJGU6* ʢ6*7JFX$POUSPMMFS3FQSFTFOUBCMF 6*7JFX3FQSFTFOUBCMFͰϥοϓͨ͠΋ͷʣͰ͸ දࣔ͞Εͳ͍ w XJOEPX4DFOFͷUPVDI#BSϓϩύςΟʹແཧ΍Γ/45PVDI#BSͷΠϯελϯεΛ౉ ͙͢Β͍͔͠ͳͦ͞͏ w

    4XJGU6*Λར༻͍ͨ͠৔߹͸$BUBMZTUͰ͸ແ͘NBD04ωΠςΟϒͷΞϓϦΛ։ൃ͠ ͨํ͕ྑ͍͔΋ w εϥΠμʔΛදݱ͢Δ/44MJEFS5PVDI#BS*UFN͔Β஋Λऔಘ͢ΔϓϩύςΟ͕ແ͍ w TMJEFS*UFNWBMVF GPS,FZEPVCMF7BMVF ͰऔಘՄೳ 43
  17. • NSToolbarItem.IdentifierΛఆٛ͢Δ • πʔϧόʔʹදࣔ͢Δ߲໨ͷछྨΛࣝผ͢Δ ࣮૷ํ๏ 47 extension NSToolbarItem.Identifier { static

    let arrowGroup = NSToolbarItem.Identifier( "me.fromkk.CatalystSampler.arrowGroup" ) static let reload = NSToolbarItem.Identifier( "me.fromkk.CatalystSampler.reload" ) static let favorite = NSToolbarItem.Identifier( "me.fromkk.CatalystSampler.favorite" ) }
  18. /45PPMCBS%FMFHBUF 49 @available(iOS 13.0, *) public protocol NSToolbarDelegate : NSObjectProtocol

    { /// itemIdentifierʹ߹ͬͨπʔϧόʔͷ߲໨Λฦ͢ optional func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? /// ॳظʹදࣔ͢ΔitemIdentifierͷҰཡΛฦ͢ optional func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] /// දࣔΛڐՄ͢ΔitemIdentifierͷҰཡΛฦ͢ optional func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] /// બ୒ՄೳͳitemIdentifierͷҰཡΛฦ͢ optional func toolbarSelectableItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] /// ߲໨͕௥Ճ͞ΕΔͱ͖ʹݺ͹ΕΔ optional func toolbarWillAddItem(_ notification: Notification) /// ߲໨͕࡟আ͞ΕΔͱ͖ʹݺ͹ΕΔ optional func toolbarDidRemoveItem(_ notification: Notification) }
  19. 61

  20. 62