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

Apple Pencil対応の勘所を話します

Shetommy
September 19, 2020

Apple Pencil対応の勘所を話します

iOSDC 2020にて発表したスライドです。
(2020/09/20 10:50〜 Track C)

---
Apple pencil、活用できてますか?

Apple pencilは、2015年末にiPad Proとともに発売され、2018年末には第二世代が発売されました。長らくApple純正アプリ以外は対応に苦慮してきたと思われますが、WWDC2019でPencilKitが発表されたことにより、簡単にApple pencil対応が可能になりました。

ライブラリ自体は使いやすいものですが、具体的なノウハウについては情報が少なくて、実際につくってみると試行錯誤することになりました。そこで本トークで、開発ノウハウを共有したいと思います。

このトークでは、PencilKitを利用したノートアプリ(「Like a Paper」)を個人開発した経験から、Apple pencil対応の勘所を話します。具体的には、PencilKitでできること/できないこと、 `PKDrawing` から `UIImage` への変換、ダークモード対応の罠、 `PKToolPicker` のカスタマイズなどを扱います。

https://fortee.jp/iosdc-japan-2020/proposal/348c2d74-7855-4d8b-8457-db9df25f9a7c

Shetommy

September 19, 2020
Tweet

More Decks by Shetommy

Other Decks in Technology

Transcript

  1. 4IFUPNJ5SBDL$
    "QQMF1FODJMରԠͷ
    צॴΛ࿩͠·͢

    View Slide

  2. 4IFUPNJ
    ࣯ ͠ͱΈ

    ʙۚ༥ܥγεςϜΤϯδχΞ
    ʙJ04ΤϯδχΞ
    Ԡԉ͍ͯ͠Δٿஂ͸౦๺ָఱΰʔϧσϯΠʔάϧε
    ˏTU2JJUB͸ͯͳOPUF
    ˏTJUXJUUFS(JUIVC

    View Slide

  3. -JLF1BQFS͸ͲΜͳΞϓϦ͔
    ͳ͔ͥͭͬͨ͘
    1FODJM,JUͱ͸
    1FODJM,JUͰͰ͖Δ͜ͱͰ͖ͳ͍͜ͱ
    1,$BOWBT7JFXͱ1,%SBXJOH
    ࡾߦͰಋೖͰ͖Δ1,$BOWBT7JFX
    1,%SBXJOHͷѻ͍ํ
    1,5PPM1JDLFSͷσβΠϯ͸͋·ΓΧελϚΠζ͕ޮ͔ͳ͍
    μʔΫϞʔυରԠͷ᠘
    ແݶεΫϩʔϧ࢓༷
    ʢ͓·͚ʣ"QQMF1FODJMରԠͷྺ࢙

    View Slide

  4. -JLF1BQFS͸ͲΜͳΞϓϦ͔

    View Slide

  5. ˡ"QQ4UPSFϦϯΫ͸ͪ͜Β͔Β

    View Slide

  6. w ʮࢴͷΑ͏ʹඳ͚Δʯ͕ίϯηϓτͷϊʔτΞϓϦ
    w "QQMF1FODJM͔ΒͷೖྗΛલఏͱ͍ͯ͠Δ
    w ॻ͍ͨϊʔτ͸ڞ༗ػೳͰଞΞϓϦͱͷ࿈ܞ͕Մೳ

    View Slide

  7. ʙ

    %-

    View Slide

  8. View Slide

  9. w μ΢ϯϩʔυͷׂ̕Ҏ্͕ӳޠݍ
    ɹʢ͋Γ͕͍͚ͨͲɺΈΜͳͲ͔ͬΒݟ͚ͭͯΜͩʁʣ
    w ೔ຊͩͱ%-͘Β͍͔͠ͳ͍ˠӳޠରԠ͸͠·͠ΐ͏ʂʂʂʂ

    View Slide

  10. ͳ͔ͥͭͬͨ͘

    View Slide

  11. View Slide

  12. View Slide

  13. ͋Εʁ
    ͳΜͰԶɺ
    J1BE1SP΋"QQMF1FODJM΋͋Μͷʹ
    ࢴʹखॻ͖ͯ͠Μͩʜʜʁ

    View Slide

  14. Կ͔Λࢥ͍ͭ͘ˠࢴʹॻ͘
    ˢͱͯ΋ࣗવ

    View Slide

  15. Կ͔Λࢥ͍ͭ͘ˠJ1BEΛखʹͱΔ
    ˠ&WFSOPUFʢ͋Δ͍͸"QQMFͷϝϞΞϓϦʁʣΛ։͘
    ˠϊʔτϒοΫΛબͿ
    ˠ৽ن࡞੒ΛબͿ
    ˠखॻ͖Ϟʔυʹ͢Δ
    ˠඳ͖࢝ΊΔ
    ˠʢλΠτϧΛܾΊΔɺͲͷϊʔτϒοΫʹೖΕΔ͔ʣ
    ˢͱͯ΋ΊΜͲ͍͘͞ʂʂʂʂʂʂʂʂʂʂʂʂʂ
    ʢͪͳΈʹݱࡏ͸&WFSOPUF͔Β/PUJPOʹ৐Γ͔͑ͯ·͢ʣ

    View Slide

  16. ϋʔυ΢ΣΞͷ໰୊Ͱ͸ͳ͘ɺ
    ιϑτ΢ΣΞͷ໰୊Ͱ͸ʁ

    View Slide

  17. ࣗ෼ͷཉ͍͠ΞϓϦ͸
    ࣗ෼Ͱͭ͘Ζ͏ʂ

    View Slide

  18. Ͱ΋"QQMF1FODJMରԠͬͯ
    Ͳ͏ͨ͠Β͑͑Μ΍ʜʜ

    View Slide

  19. 1FODJM,JU

    View Slide

  20. 1FODJM,JUͱ͸

    View Slide

  21. w 88%$Ͱొ৔ͨ͠ϑϨʔϜϫʔΫ
    w "QQMF1FODJMPSࢦͰͷखॻ͖ೖྗΛ૝ఆͯͭ͘͠ΒΕ͍ͯΔ
    w ؆୯ʹ"QQMF७ਖ਼ͷϝϞΞϓϦ-JLFͳڍಈ͕࣮ݱͰ͖Δ
    w J04͔Β৽ػೳ͕௥Ճ͞ΕɺࠓҰ൪஫໨ΛूΊΔϥΠϒϥϦ
    ɹʢ৽ػೳʣɹ˞ຊηογϣϯͰ͸࣌ؒͷؔ܎Ͱѻ͑ͣ
    ɹ4DSJCCMFςΩετϑΟʔϧυʹखॻ͖ͰจࣈΛॻ͘ͱɺςΩετσʔλͱͯ͠ೝࣝ͞ΕΔ
    ɹ1,4USPLFखॻ͖ͷيಓ͕औಘͰ͖Δ

    View Slide

  22. ࠓҰ൪஫໨ΛूΊΔϥΠϒϥϦ

    View Slide

  23. 1FODJM,JUͰ
    Ͱ͖Δ͜ͱͰ͖ͳ͍͜ͱ

    View Slide

  24. Ͱ͖Δ͜ͱ
    w "QQMF७ਖ਼ͷϝϞΞϓϦͷ6*Λ࣮ݱ͢Δ
    w $BOWBT
    w 5PPM1JDLFSVOEPSFEP ϖϯফ͠ΰϜఆن ৭มߋ
    w 1,%SBXJOHͱ6**NBHF%BUBܕͷ૬ޓม׵Λ͢Δ
    w ࢦͱ"QQMF1FODJMͷλονΛࣝผ͢Δ
    ɹʢͨͩ͠BMMPXT'JOHFS%SBXJOH͕J04͔Β%FQSFDBUFEʹͳ͍ͬͯΔʣ
    w %FMFHBUFϝιουܦ༝ͰESBXJOHͷมߋɺ
    5PPM1JDLFSͷ࢖༻ͷTUBSUFOEΛݕ஌͢Δ

    View Slide

  25. Ͱ͖ͳ͍͜ͱ
    w 6*ͷΧελϚΠζ
    w 5PPM1JDLFS͸ Ͱݻఆ
    w "QQMF७ਖ਼ϝϞʹͳ͍ػೳͷ௥Ճ
    w ͨͱ͑͹ʮృΓ௵͕ͭ͘͠Γ͍ͨʯͱࢥͬͯ΋ɺ
    ɹ֦ுੑ͸ͳ͍ͷͰɺ׬શʹผݸͰͭ͘Δඞཁ͕͋Δ

    View Slide

  26. 1,$BOWBT7JFXͱ1,%SBXJOH

    View Slide

  27. 1,$BOWBT7JFX
    1,%SBXJOH
    1,%SBXJOH
    1,%SBXJOH
    7JFX .PEFM
    ϝΠϯ
    .drawingʹࢦఆ

    View Slide

  28. 1,$BOWBT7JFX 1,%SBXJOH
    7JFX .PEFM
    πʔϧ
    1,5PPM1JDLFS
    1,%SBXJOH
    1,%SBXJOH
    1,*OLJOH5PPM
    PKToolPickerObserver
    (protocol)
    1,&SBTFS5PPM
    1,-BTTP5PPM
    1,5PPM
    .selectedTool
    ˞1,$BOWBT7JFXͷAUPPMAʹ௚ࢦఆ΋Մ

    View Slide

  29. ࡾߦͰಋೖͰ͖Δ1,$BOWBT7JFX

    View Slide

  30. let canvas = PKCanvasView(frame: view.frame)
    view.addSubview(canvas)
    canvas.tool = PKInkingTool(.pen, color: .black, width: 30)

    View Slide

  31. ͨͬͨ3ߦͷίʔυͰ PencilKit Λಋೖͯ͠ Apple Pencil ରԠ(https://qiita.com/niwasawa/items/d8e239cd23666c750a2f)

    View Slide

  32. &BTZUPVTF

    View Slide

  33. 1,%SBXJOHͷѻ͍ํ

    View Slide

  34. 1,%SBXJOHͷجຊ
    w ಺෦ߏ଄͸Ṗ
    w 1,$BOWBT7JFXͷΠϯϓοτΞ΢τϓοτ͸શͯ1,%SBXJOHܕܦ༝ͱͳΔ
    w 6**NBHFʹϝιουҰൃͰม׵Մೳ
    ɹˠނʹଞΞϓϦͱͷڞ༗͸ҙ֎ͱָͩͬͨ
    w EBUB3FQSFTFOUBUJPO
    Ͱ%BUBܕʹҰൃͰม׵Մೳ
    w CPVOETΛ͍࣋ͬͯͯɺ$BOWBT7JFXͷαΠζͱ͸ผݸʹࣗ෼ͷαΠζΛ࣋ͭ

    View Slide

  35. %BUBͰͦͷ··อଘ͢Δͷ͸ѱख
    w 1,%SBXJOH͕ෳ਺ʹͳΔͱɺ%BUBͩͱϝλσʔλΛѻ͍੾Εͳ͍
    ʢFYʣॱংɺඳ͍ͨ೔࣌ɺछྨͳͲ
    w ެࣜαϯϓϧͩͱҰݸט·͍ͤͯͨͷͰɺͦΕʹͳΒͬͨ
    struct DataModel: Codable {
    /// Names of the drawing assets to be used to initialize the data model the first time.
    static let defaultDrawingNames: [String] = ["Notes"]
    /// The width used for drawing canvases.
    static let canvasWidth: CGFloat = 768
    /// The drawings that make up the current data model.
    var drawings: [PKDrawing] = []
    var signature = PKDrawing()
    }

    View Slide

  36. J04ΞϓϦؒͳΒ1,%SBXJOH౉ͤΔʁ
    ʢະ֬ೝʣ
    w ݁ہڞ༗͢Δࡍ͸6**NBHFͰ࿈ܞ͍ͯ͠Δ
    w ΋͠ڞ༗ઌͷΞϓϦ΋1FODJM,JU࢖͍ͬͯͨΒɺ
    ɹ1,%SBXJOHΛ౉ͯ͠ɺڞ༗ઌͷΞϓϦͰฤूΛܧଓͰ͖Δʁ
    w 6*"DUJWJUZ7JFX$POUSPMMFSͷBDUJWJUZ*UFN͸"OZͳͷͰɺ
    ɹޓ͍ͷΞϓϦͷܕೝ͕ࣝ߹͍ͬͯΕ͹ड͚౉͠Ͱ͖ͦ͏ͳؾ͕͢Δ
    w ΋͠ࢼͨ͠ํ͕͍ͨΒ৘ใ͍ͩ͘͞ʂ

    View Slide

  37. 1,5PPM1JDLFSͷσβΠϯ͸
    ͋·ΓΧελϚΠζ͕ޮ͔ͳ͍

    View Slide

  38. 1,5PPM1JDLFSͷઃఆྫ
    private func addPalette() {
    if let window = UIApplication.shared.windows.first,
    let toolPicker = PKToolPicker.shared(for: window) {
    self.toolPicker = toolPicker
    self.toolPicker.addObserver(canvasView)
    self.toolPicker.addObserver(self)
    canvasView.becomeFirstResponder()
    self.toolPicker.selectedTool = PKInkingTool(.pen, color: .black, width: 1)
    }
    }

    View Slide

  39. μʔΫϞʔυରԠͷ᠘

    View Slide

  40. View Slide

  41. View Slide

  42. w 1FODJM,JU͸J04͔Βొ৔ͨ͠ϑϨʔϜϫʔΫ
    w J04ͱ͍͑͹μʔΫϞʔυॳొ৔
    w 1FODJM,JUࣗମ͕μʔΫϞʔυΛߟྀͯ͠ઃܭ͞Ε͍ͯΔ
    w ނʹಛʹࢦఆ͠ͳͯ͘΋ͳΜ্͔ख͍͜ͱ΍ͬͯ͘ΕΔ

    View Slide

  43. ͔͠͠໌ࣔతʹࢦఆ͍ͯ͠ͳ͍ͨΊɺ
    ։ൃऀͷҙਤ͠ͳ͍ڍಈʹͳΔʢͳͬͨʣ

    View Slide

  44. ʂʁ

    View Slide

  45. Կ͕ى͖ͨʁ
    w $PMMFDUJPO7JFXʹԼهͷΑ͏ʹϊʔτΛද͍ࣔͯͨ͠
    let drawing = drawings[indexPath.row]
    let image = drawing.image(from: drawing.bounds, scale: 1.0)
    w දࣔޙʹϥΠτϞʔυμʔΫϞʔυͷ੾Γସ͕͑ى͜Δͱɺ
    ɹ৭൓స͕ߦ͑ͣɺݟ͑ͳ͘ͳͬͯ͠·͏

    View Slide

  46. override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    if previousTraitCollection?.userInterfaceStyle != traitCollection.userInterfaceStyle {
    reload()
    }
    }
    w ରࡦˣ
    w ͳ͓ڞ༗ػೳͰόά͕·ͩ࢒͍ͬͯΔʜʜ
    w 1FODJM,JUʹμʔΫϞʔυରԠ͓೚ͤͰ͖ΔΑ͏Ͱ
    ɹ࣮͸΍Βͳ͖Ό͍͚ͳ͍͜ͱ͕͋Δ

    View Slide

  47. ແݶεΫϩʔϧ࢓༷

    View Slide

  48. ஌ͬͯ·ͨ͠ʁ
    w "QQMFͷ७ਖ਼ϝϞ͸ॎํ޲ʹແݶεΫϩʔϧͰ͖Δ

    View Slide

  49. 1,$BOWBTΛແݶεΫϩʔϧՄʹ͢Δ
    canvasView.alwaysBounceVertical = true

    View Slide

  50. // MARK: Canvas View Delegate
    /// Delegate method: Note that the drawing has changed.
    func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
    hasModifiedDrawing = true
    updateContentSizeForDrawing()
    }
    /// Helper method to set a suitable content size for the canvas view.
    func updateContentSizeForDrawing() {
    // Update the content size to match the drawing.
    let drawing = canvasView.drawing
    let contentHeight: CGFloat
    // Adjust the content size to always be bigger than the drawing height.
    if !drawing.bounds.isNull {
    contentHeight = max(canvasView.bounds.height, (drawing.bounds.maxY +
    DrawingViewController.canvasOverscrollHeight) * canvasView.zoomScale)
    } else {
    contentHeight = canvasView.bounds.height
    }
    canvasView.contentSize = CGSize(width: DataModel.canvasWidth * canvasView.zoomScale, height: contentHeight)
    }
    ˞88%$ͷαϯϓϧΑΓ

    View Slide

  51. ʢ͓·͚ʣ
    "QQMF1FODJMରԠͷྺ࢙

    View Slide

  52. ˞1FODJM,JUొ৔Ҏલ
    ʙJ046*,JUͷ6*5PVDIͰ৭ʑؤு͍ͬͯͨʁ
    J04ʙ6*1FODJM*OUFSBDUJPO
    &WFSOPUFͷखॻ͖7JFXˠ
    ʢ4LJUDI-JLFͳ΍ͭʣ
    ͸͓ͦΒ͘6*,JUͰؤுͬͨʁ

    View Slide

  53. ·ͱΊ

    View Slide

  54. w 1FODJM,JUΛ࢖͏ͱ؆୯ʹ"QQMF1FODJMରԠ͕Ͱ͖Δ
    w ͨͩ͠"QQMF७ਖ਼ϝϞΞϓϦͷ࢓༷͔ΒΧελϚΠζੑ͸͞΄Ͳͳ͍
    w μʔΫϞʔυʹউखʹରԠͯ͘͠ΕΔͷ͕ศར
    w ७ਖ਼ϝϞΞϓϦͷઃܭ΋Ԟ͕ਂ͍

    View Slide

  55. ࢀߟɿ
    ɾ*OUSPEVDJOH1FODJM,JU 88%$
    IUUQTEFWFMPQFSBQQMFDPNWJEFPTQMBZXXED
    ɾ"QQMF1FODJMͰͷϝϞΛؾܰʹ͢ΔΞϓϦʮ-JLFB1BQFSʯΛݸਓ։ൃͨ͠ͷͰେมͩͬͨ͜ͱΛڞ༗
    ͠·͢ʢ࣯ͷ2JJUBهࣄʣIUUQTRJJUBDPNTUJUFNTGDCBD
    Ͳ͏ͯ͠΋1FODJM,JU࢖͍ͨ͘ͳ͍Կ͔͕͋Δਓʢ಺༰ະ֬ೝʣɿ
    ɾ-FWFSBHJOH5PVDI*OQVUGPS%SBXJOH"QQTɹIUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOVJLJU
    [email protected]@[email protected]@[email protected]@[email protected]@BQQT

    View Slide