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

noteのiOSアプリで実装したアクセシビリティの全て #iosdc #a /a11y_with_iOS_App_on_note

fromkk
September 18, 2021

noteのiOSアプリで実装したアクセシビリティの全て #iosdc #a /a11y_with_iOS_App_on_note

iOSDC Japan 2021で登壇した資料です。

----CfP----
noteのiOSアプリはここ1年間で大きな変化を遂げました。
元々30%程度だったSwift率が90%を超え、デバイスもiPhoneの縦画面のみの対応でしたがiPadの複数ウィンドウにも対応しました。
このように目に見えるカイゼンを進めてきた私たちですが、あるきっかけで目に見えない課題があることを知りました。
アクセシビリティ機能を駆使して私たちのアプリを利用してくれている方からお問い合わせをいただいたのです。
この方の日頃の利用方法についてインタビューさせていただくことになり、今まで見えていなかった課題に気づくことができました。
そこでアクセシビリティの有識者に力を借りながら、Voice OverやDynamic Typeといったアクセシビリティの助けとなる機能にも対応をはじめました。
・Voice Overを利用するとさわれると思っているUI要素にさわれない
・わかりやすいと思っていたボタンなのにVoice Overを通すと何のボタンなのか理解ができない
このトークではこういった課題を認知して、実際に対応したことをまとめてお話します。

fromkk

September 18, 2021
Tweet

More Decks by fromkk

Other Decks in Programming

Transcript

  1. note inc.


    ೥݄ελʔτ
    4

    View full-size slide

  2. note inc.


    cakes͸ଟ਺ͷΫϦΤΠλʔ΍ग़൛ࣾͱఏܞ͍ͯ͠Δίϯςϯπ഑৴αΠτ
    Ͱ͢ɻಡऀͱΫϦΤΠλʔΛΑΓ਎ۙʹ݁ͼ͚ͭΔ͜ͱͰɺίϯςϯπͱͷ
    ৽͍͠ग़ձ͍ͷ৔Λఏڙ͠·͢ɻ
    5

    View full-size slide

  3. note inc.


    ೥݄ελʔτ
    6

    View full-size slide

  4. note inc.


    ΫϦΤΠλʔ͕จষ΍ը૾ɺԻ੠ɺಈըΛ౤ߘͯ͠ɺϢʔβʔ͕ͦͷίϯςϯπΛ
    ָ͠ΜͰԠԉͰ͖ΔϝσΟΞϓϥοτϑΥʔϜɻͩΕ΋͕૑࡞Λָ͠ΜͰଓ͚ΒΕ
    ΔΑ͏ɺ҆৺Ͱ͖Δงғؾ΍ɺଟ༷ੑΛେ੾ʹ͍ͯ͠·͢ɻ
    7

    View full-size slide

  5. note inc.


    ϛογϣϯ
    ͩΕ΋͕૑࡞Λ͸͡Ίɺ
    ଓ͚ΒΕΔΑ͏ʹ͢Δɻ
    8

    View full-size slide

  6. ΞΫηγϏϦςΟ
    9

    View full-size slide

  7. ΢ΣϒΞΫηγϏϦςΟج൫ҕһձ

    https://waic.jp/knowledge/accessibility/
    “ҰൠʹΞΫηγϏϦςΟͱ͸ɺΞΫηεͷ͠΍͢͞Λҙຯ͠·͢ɻస
    ͯ͡ɺ੡඼΍αʔϏεͷར༻͠΍͢͞ͱ͍͏ҙຯͰ΋࢖ΘΕ·͢ɻ”
    ΞΫηγϏϦςΟ
    10

    View full-size slide

  8. ΞΫηγϏϦςΟ
    • ো֐ऀ΍ߴྸऀ޲͚ͷରԠͱࢥΘΕ͕͕ͪͩɺશਓྨ͕৘ใʹ৮ΕͨΓػೳΛମݧ
    Ͱ͖Δ͜ͱΛ໨ࢦ͢


    • ಛʹը໘Λཁ͢Δ΢ΣϒαΠτ΍εϚʔτϑΥϯΞϓϦέʔγϣϯʹ͓͍ͯ͸ɺࢹ֮
    ো֐Λ࣋ͭਓ΍ࢹྗ͕௿Լͨ͠ਓʹෆศΛײͤͯ͡͞͠·͏͜ͱ͕ଟ͍ͨΊɺࢹ֮
    తͳαϙʔτΛߦ͏͜ͱ͕த৺ͱͳΔ


    • ྫ: ը໘ͷಡΈ্͛ػೳ΍ɺը໘΍ϑΥϯτͷ֦େػೳΛαϙʔτ


    • ͜ͷൃදͰ΋AppleϓϥοτϑΥʔϜͷΞΫηγϏϦςΟʹؔ͢ΔػೳͰ͋Δ
    VoiceOverΛத৺ʹ঺հ
    11

    View full-size slide

  9. ΢ΣϒΞΫηγϏϦςΟͷ4ͭͷݪଇ
    • ஌֮Մೳ


    • ৘ใٴͼϢʔβΠϯλϑΣʔείϯϙʔωϯτ͸ɺར༻ऀ͕஌֮Ͱ͖Δํ๏Ͱར༻ऀʹఏࣔՄೳͰͳ
    ͚Ε͹ͳΒͳ͍ɻ


    • ૢ࡞Մೳ


    • ϢʔβΠϯλϑΣʔείϯϙʔωϯτٴͼφϏήʔγϣϯ͸ૢ࡞ՄೳͰͳ͚Ε͹ͳΒͳ͍ɻ


    • ཧղՄೳ


    • ৘ใٴͼϢʔβΠϯλϑΣʔεͷૢ࡞͸ཧղՄೳͰͳ͚Ε͹ͳΒͳ͍ɻ


    • ݎ࿚ੑ


    • ίϯςϯπ͸ɺࢧԉٕज़ΛؚΉ༷ʑͳϢʔβΤʔδΣϯτ͕࣮֬ʹղऍͰ͖ΔΑ͏ʹे෼ʹݎ࿚Ͱͳ
    ͚Ε͹ͳΒͳ͍ɻ
    12
    https://waic.jp/docs/UNDERSTANDING-WCAG20/intro.html

    View full-size slide

  10. ϢʔβʔϏϦςΟͱͷؔ܎
    • ࢖͍΍͢͞ͱ͍͏ίϯςΩετͰΑ͘ར༻͞ΕΔ͕ϢʔβʔϏ
    ϦςΟ͸ΞΫηγϏϦςΟ͕อͨΕ্ͨͰҙ͍ࣝͨ͠


    • ͦ΋ͦ΋ΞΫηεͰ͖ͳ͚Ε͹ར༻͕Ͱ͖ͳ͍ͨΊ
    13

    View full-size slide

  11. ϢʔβʔϏϦςΟͱͷؔ܎
    • ࢖͍΍͢͞ͱ͍͏ίϯςΩετͰΑ͘ར༻͞ΕΔ͕ϢʔβʔϏ
    ϦςΟ͸ΞΫηγϏϦςΟ͕อͨΕ্ͨͰҙ͍ࣝͨ͠


    • ͦ΋ͦ΋ΞΫηεͰ͖ͳ͚Ε͹ར༻͕Ͱ͖ͳ͍ͨΊ
    14
    ϢʔβʔϏϦςΟ
    ΞΫηγϏϦςΟ

    View full-size slide

  12. ΠϯΫϧʔγϒσβΠϯ
    • ॅΜͰ͍Δ৔ॴ΍೥୅ɺੑผͳͲʹΑΔจԽͷҧ͍


    • ࿩ͨ͠Γฉ͍ͨΓɺॻ͍ͨΓಡΜͩΓ͢Δݴޠ
    15
    https://developer.apple.com/videos/play/wwdc2021/10275/
    https://developer.apple.com/videos/play/wwdc2021/10304/
    ͳͲ͜Ε·Ͱഉআ͞Ε͍ͯͨਓʑΛר͖ࠐΈͳ͕ΒแׅతʹσβΠϯ͢Δ
    ख๏
    ΞΫηγϏϦςΟ΍ϢʔβʔϏϦςΟ
    +

    View full-size slide

  13. ͳͥ ͰΞΫηγϏϦςΟ
    ͷ޲্ʹऔΓ૊Έ࢝Ί͔ͨ
    16

    View full-size slide

  14. औΓ૊Έ࢝Ί͖͔͚ͨͬ
    • ࣾ಺͔ΒΞΫηγϏϦςΟΛ޲্͍ͨ͠ͱ͍͏੠্͕͕Δ


    • ΋ͱ΋ͱΞΫηγϏϦςΟͷ޲্ʹڵຯ͕͋ͬͨ


    • ۀ຿ҕୗͱ͍͏ܗͰΞΫηγϏϦςΟʹؔ͢ΔΞυόΠεΛ͘ΕΔํ͕
    δϣΠϯ


    • noteͷ7प೥ه೦ΠϕϯτͰΞΫηγϏϦςΟʹ΋ྗΛೖΕΔࢫΛൃද


    • ໡໨ͷํ͔Βͷ໰͍߹Θ͕ͤ͋ΓϢʔβʔΠϯλϏϡʔΛ࣮ࢪ
    17

    View full-size slide

  15. औΓ૊Έ࢝Ί͖͔͚ͨͬ
    • ࣾ಺͔ΒΞΫηγϏϦςΟΛ޲্͍ͨ͠ͱ͍͏੠্͕͕Δ


    • ΋ͱ΋ͱΞΫηγϏϦςΟͷ޲্ʹڵຯ͕͋ͬͨ


    • ۀ຿ҕୗͱ͍͏ܗͰΞΫηγϏϦςΟʹؔ͢ΔΞυόΠεΛ͘ΕΔํ͕
    δϣΠϯ


    • noteͷ7प೥ه೦ΠϕϯτͰΞΫηγϏϦςΟʹ΋ྗΛೖΕΔࢫΛൃද


    • ໡໨ͷํ͔Βͷ໰͍߹Θ͕ͤ͋ΓϢʔβʔΠϯλϏϡʔΛ࣮ࢪ
    18

    View full-size slide

  16. ໡໨ͷํ΁ͷΠϯλϏϡʔΛ࣮ࢪ
    • ZoomͰ࣮ࢪ


    • ීஈͲͷΑ͏ʹnoteΛར༻͍ͯ͠Δ͔


    • noteͰࠔ͍ͬͯΔ͜ͱ͕ͳ͍͔
    19

    View full-size slide

  17. ໡໨ͷํ΁ͷΠϯλϏϡʔΛ࣮ࢪ
    • ZoomͰ࣮ࢪ


    • ීஈͲͷΑ͏ʹnoteΛར༻͍ͯ͠Δ͔


    • noteͰࠔ͍ͬͯΔ͜ͱ͕ͳ͍͔


    • iOSΞϓϦͰهࣄʹݟग़͠ը૾͕ઃఆͰ͖ͳ͍


    • هࣄΛެ։͢Δࡍʹϋογϡλάͷઃఆ͕͏·͍͔͘ͳ͍
    20

    View full-size slide

  18. هࣄʹݟग़͠ը૾͕ઃఆͰ͖ͳ͍༷ࢠ
    21

    View full-size slide

  19. هࣄΛެ։͢Δࡍʹϋογϡλάͷઃఆ͕͏·͍͔͘ͳ͍༷ࢠ
    22

    View full-size slide

  20. ͜ΕΒͷ༷ࢠΛݟͯࢥ͍·ͨ͠
    23

    View full-size slide

  21. όά΍Μ…
    24

    View full-size slide

  22. ଊ͑ํͷมԽ
    • ͜Ε·Ͱ͸ΞΫηγϏϦςΟΛ޲্ͤ͞Α͏ͱࢥͬͯ΋Ͳ͏ߦಈ͠
    ͍͍ͯͷ͔෼͔Βͳ͔ͬͨ


    • ࣮ࡍʹಈ༷͘ࢠΛݟΔ͜ͱͰɺͨͩͷෆ۩߹ͩͱଊ͑ΒΕΔΑ͏ʹ


    • ΞΫηγϏϦςΟ޲্Λܝ͛ͯ΋ͳ͔ͳ͔λεΫ͸ਐΈਏ͍


    • 1ͭͷόάमਖ਼ͱ͍͏λεΫʹ෼ղ͢Ε͹༏ઌ౓্͕͕ͬͯ͘Δ
    25

    View full-size slide

  23. VoiceOverͷ࢖͍ํ
    26

    View full-size slide

  24. VoiceOverͷ੾Γସ͑ํ๏
    27

    View full-size slide

  25. VoiceOverͷ؆୯ͳ࢖͍ํ
    28

    View full-size slide

  26. https://developer.apple.com/videos/play/wwdc2018/226/
    “VoiceOver: App Testing Beyond The Visuals”
    29

    View full-size slide

  27. ࣮ࡍʹൃੜͨ͠໰୊ͱͦͷղܾํ๏
    30

    View full-size slide

  28. ࣮ࡍʹൃੜͨ͠໰୊ͱͦͷղܾํ๏
    • tapGestureRecognizer͕ઃఆ͞Ε͍ͯΔUIView͕બ୒Ͱ
    ͖ͳ͍໰୊


    • ը૾ͷΈͷϘλϯͰҙਤ͠ͳ͍ಡΈ্͕͛͞Εͯ͠·͏໰୊


    • ϋʔϑϞʔμϧ͕ด͡ΒΕͳ͍໰୊
    31

    View full-size slide

  29. tapGestureRecognizer͕ઃఆ͞
    Ε͍ͯΔUIView͕બ୒Ͱ͖ͳ͍໰୊
    32

    View full-size slide

  30. ίʔυΠϝʔδ
    lazy var containerView: UIView = {


    let view = UIView(frame: view.bounds)


    view.isUserInteractionEnabled = true


    view.addGestureRecognizer(


    UITapGestureRecognizer(target: self, action: #selector(tap(sender:))))


    return view


    }()


    @objc private func tap(sender: UITapGestureRecognizer) {


    showTappedAlert()


    }
    33

    View full-size slide

  31. tapGestureRecognizer͕


    ઃఆ͞Ε͍ͯΔUIView͕બ୒Ͱ͖ͳ͍໰୊
    34

    View full-size slide

  32. मਖ਼಺༰
    view.isAccessibilityElement = true
    +
    35

    View full-size slide

  33. tapGestureRecognizer͕


    ઃఆ͞Ε͍ͯΔUIView͕બ୒Ͱ͖ͳ͍໰୊मਖ਼൛
    36

    View full-size slide

  34. ը૾ͷΈͷϘλϯͰҙਤ͠ͳ͍
    ಡΈ্͕͛͞Εͯ͠·͏໰୊
    37

    View full-size slide

  35. ίʔυΠϝʔδ
    lazy var button: UIButton = {


    let button = UIButton(type: .custom)


    let configuration = UIImage.SymbolConfiguration(


    font: UIFont.systemFont(ofSize: 280))


    let image = UIImage(systemName: "heart", withConfiguration: configuration)


    button.setImage(image, for: .normal)


    return button


    }()
    38

    View full-size slide

  36. ը૾ͷΈͷϘλϯ͕


    ҙਤ͠ͳ͍ಡΈ্͕͛͞Εͯ͠·͏໰୊
    39

    View full-size slide

  37. मਖ਼಺༰
    button.accessibilityLabel = "εΩ"
    +
    40

    View full-size slide

  38. ը૾ͷΈͷϘλϯ͕


    ҙਤ͠ͳ͍ಡΈ্͕͛͞Εͯ͠·͏໰୊मਖ਼൛
    41

    View full-size slide

  39. ϋʔϑϞʔμϧ͕ด͡ΒΕͳ͍໰୊
    42

    View full-size slide

  40. ϋʔϑϞʔμϧ͕ด͡ΒΕͳ͍໰୊
    43

    View full-size slide

  41. मਖ਼಺༰ 1
    lazy var closeButton: UIButton = {


    let button = UIButton(type: .custom)


    button.setTitle("ด͡Δ", for: .normal)


    button.addTarget(self, action: #selector(close(sender:)), for: .touchUpInside)


    return button


    }()


    @objc private func close(sender: Any) {


    dismiss(animated: true)


    }
    44

    View full-size slide

  42. मਖ਼಺༰ 2
    private var cancellable: AnyCancellable?


    private func subscribeVoiceOver() {


    cancellable?.cancel()


    cancellable = NotificationCenter.default.publisher(


    for: UIAccessibility.voiceOverStatusDidChangeNotification


    )


    .sink { [weak self] _ in


    self?.handleVoiceOverStatus()


    }


    }


    private func handleVoiceOverStatus() {


    closeButton.isHidden = !UIAccessibility.isVoiceOverRunning


    }
    45

    View full-size slide

  43. ϋʔϑϞʔμϧ͕


    ด͡ΒΕΔΑ͏ʹमਖ਼൛
    46

    View full-size slide

  44. ߋʹମݧΛ޲্ͤ͞Δ6ͭͷࢪࡦ
    47

    View full-size slide

  45. ߋʹମݧΛ޲্ͤ͞Δ6ͭͷࢪࡦ
    • ϑΥʔΧε͕౰͍ͨͬͯΔཁૉͰԿ͕Ͱ͖Δͷ͔Λ஌Β͍ͤͨ


    • Կ͔ಈ࡞Λͨ͠Β༻ҙͨ͠ςΩετΛಡΈ্͍͛ͨ


    • Կ͔ಈ࡞Λͨ͠ࡍʹϑΥʔΧε͞ΕΔ৔ॴΛࢦఆ͍ͨ͠


    • ೔෇ͷಡΈ্͛ΛࣗવʹରԠ͍ͨ͠


    • Ϧετը໘ͰεΠονͷON/OFFͷૢ࡞Λࣗવʹ͍ͨ͠


    • ݟग़͠δϟϯϓػೳ΁ͷରԠΛ͍ͨ͠
    48

    View full-size slide

  46. ϑΥʔΧε͕౰͍ͨͬͯΔཁૉͰ
    Կ͕Ͱ͖Δͷ͔Λ஌Β͍ͤͨ
    49

    View full-size slide

  47. 50
    Կ͕Ͱ͖Δͷ͔͕෼͔Βͳ͍༷ࢠ

    View full-size slide

  48. ରԠ಺༰
    51
    cell.accessibilityHint = "μϒϧλοϓͰ࡟আ"
    +

    View full-size slide

  49. 52
    Կ͕Ͱ͖Δͷ͔͕෼͔ΔΑ͏ʹͳ༷ͬͨࢠ

    View full-size slide

  50. Կ͔ಈ࡞Λͨ͠Β༻ҙͨ͠ςΩετ
    ΛಡΈ্͍͛ͨ
    53

    View full-size slide

  51. ίʔυΠϝʔδ
    54
    private func showErrorMessage(_ errorMessage: String) {


    errorLabel.text = errorMessage


    }

    View full-size slide

  52. 55
    Τϥʔ͕දࣔ͞Εͯ΋


    ؾ͚ͮͳ͍༷ࢠ

    View full-size slide

  53. ରԠ಺༰
    56
    private func showErrorMessage(_ errorMessage: String) {


    errorLabel.text = errorMessage


    UIAccessibility.post(notification: .announcement, argument: errorMessage)


    }
    +
    https://developer.apple.com/documentation/uikit/uiaccessibility/notification

    View full-size slide

  54. 57
    Τϥʔ͕දࣔ͞Ε͍ͯΔ͜ͱʹ


    ؾ͚ͮΔΑ͏ʹͳ༷ͬͨࢠ

    View full-size slide

  55. Կ͔ಈ࡞Λͨ͠ࡍʹϑΥʔΧε
    ͞ΕΔ৔ॴΛࢦఆ͍ͨ͠
    58

    View full-size slide

  56. Կ͔ಈ࡞Λͨ͠ࡍʹϑΥʔΧε͞ΕΔ৔ॴΛࢦఆ͍ͨ͠
    59

    View full-size slide

  57. मਖ਼಺༰
    @objc private func next(sender: Any) {


    UIAccessibility.post(notification: .layoutChanged, argument: messageLabel)


    }
    +
    60
    https://developer.apple.com/documentation/uikit/uiaccessibility/notification

    View full-size slide

  58. Կ͔ಈ࡞Λͨ͠ࡍʹϑΥʔΧε͞ΕΔ৔ॴΛࢦఆ͍ͨ͠ରԠ൛
    61

    View full-size slide

  59. ೔෇ͷಡΈ্͛ΛࣗવʹରԠ͍ͨ͠
    62

    View full-size slide

  60. ίʔυΠϝʔδ1
    lazy var dateLabel: UILabel = {


    let label = UILabel()


    label.textColor = .label


    label.font = UIFont.preferredFont(forTextStyle: .headline)


    label.textAlignment = .center


    label.numberOfLines = 0


    label.lineBreakMode = .byWordWrapping


    return label


    }()


    private func showNow() {


    dateLabel.text = dateFormatter.string(from: now)


    }
    63

    View full-size slide

  61. ίʔυΠϝʔδ2
    private lazy var dateFormatter: DateFormatter = {


    guard


    let dateFormat = DateFormatter.dateFormat(


    fromTemplate: "yyyy/MM/dd HH:mm", options: 0, locale: .current)


    else {


    fatalError("failed get date format from template")


    }


    var calendar = Calendar(identifier: .gregorian)


    let timeZone = TimeZone.current


    let locale = Locale.current


    calendar.locale = locale


    var dateFormatter = DateFormatter()


    dateFormatter.calendar = calendar


    dateFormatter.timeZone = timeZone


    dateFormatter.locale = locale


    dateFormatter.dateFormat = dateFormat


    return dateFormatter


    }()
    64

    View full-size slide

  62. 65
    ೔෇ͷಡΈ্͛ΛࣗવʹରԠ͢Δલ

    View full-size slide

  63. मਖ਼಺༰
    66
    private func showNow() {


    dateLabel.text = dateFormatter.string(from: now)


    dateLabel.accessibilityLabel = accessibilityDateFormatter.string(from: now)


    }


    private lazy var accessibilityDateFormatter: DateFormatter = {


    var calendar = Calendar(identifier: .gregorian)


    let timeZone = TimeZone.current


    let locale = Locale.current


    calendar.locale = locale


    var dateFormatter = DateFormatter()


    dateFormatter.calendar = calendar


    dateFormatter.timeZone = timeZone


    dateFormatter.locale = locale


    dateFormatter.dateStyle = .long


    dateFormatter.timeStyle = .medium


    return dateFormatter


    }()
    +
    +

    View full-size slide

  64. 67
    ೔෇ͷಡΈ্͛ΛࣗવʹରԠ൛

    View full-size slide

  65. Ϧετը໘ͰεΠονͷON/OFFͷૢ࡞
    Λࣗવʹ͍ͨ͠
    68

    View full-size slide

  66. 69
    Ϧετը໘ͰεΠονͷON/OFFͷૢ࡞վળલ

    View full-size slide

  67. मਖ਼಺༰
    70
    private func setUp() {


    isAccessibilityElement = true


    }


    override var accessibilityLabel: String? {


    get {


    titleLabel.text


    }


    set {}


    }


    override var accessibilityValue: String? {


    get {


    toggleSwitch.accessibilityValue


    }


    set {}


    }


    override var accessibilityTraits: UIAccessibilityTraits {


    get {


    toggleSwitch.accessibilityTraits


    }


    set {}


    }


    override func accessibilityActivate() -> Bool {


    toggleSwitch.isOn.toggle()


    return true


    }
    SwitchAccessibleViewCell.swift

    View full-size slide

  68. 71
    Ϧετը໘ͰεΠονͷON/OFFͷૢ࡞վળޙ

    View full-size slide

  69. ݟग़͠δϟϯϓػೳ΁ͷରԠΛ͍ͨ͠
    72

    View full-size slide

  70. ϩʔλʔػೳ
    • “VoiceOver ϩʔλʔΛ࢖ͬͯ VoiceOver ͷಈ࡞ΛมߋͰ͖·
    ͢ɻVoiceOver ͷԻྔ΍࿩͢଎͞Λมߋͨ͠Γɺը໘্Ͱ߲໨ؒ
    ΛҠಈͨ͠Γ͢Δ͜ͱ͕Ͱ͖ɺͦͷଞͷૢ࡞΋ՄೳͰ͢ɻ”

    https://support.apple.com/ja-jp/HT204783


    • 1ຊࢦͷ্ԼεϫΠϓͰར༻Մೳ


    • ը໘্Λ2ຊࢦͰճ͢Α͏ʹಈ͔͢͜ͱͰઃఆΛมߋ


    • ΧελϜػೳΛ࡞੒͢Δ͜ͱ΋Մೳ

    https://developer.apple.com/documentation/uikit/
    uiaccessibilitycustomrotor
    73

    View full-size slide

  71. ίʔυΠϝʔδ
    74
    private func makeHeadlineLabel(with title: String) -> UILabel {


    let label = UILabel()


    label.font = UIFont.preferredFont(forTextStyle: .headline)


    label.textColor = UIColor.label


    label.text = title


    label.numberOfLines = 0


    label.lineBreakMode = .byWordWrapping


    return label


    }

    View full-size slide

  72. 75
    ݟग़͠δϟϯϓػೳ΁ͷରԠલ

    View full-size slide

  73. मਖ਼಺༰
    76
    private func makeHeadlineLabel(with title: String) -> UILabel {


    let label = UILabel()


    label.font = UIFont.preferredFont(forTextStyle: .headline)


    label.textColor = UIColor.label


    label.text = title


    label.numberOfLines = 0


    label.lineBreakMode = .byWordWrapping


    label.accessibilityTraits = [.header]


    return label


    }
    +

    View full-size slide

  74. 77
    ݟग़͠δϟϯϓػೳ΁ͷରԠޙ

    View full-size slide

  75. 78
    static var none: UIAccessibilityTraits
    The accessibility element has no traits.


    static var button: UIAccessibilityTraits
    The accessibility element behaves like a button.


    static var link: UIAccessibilityTraits
    The accessibility element behaves like a link.


    static var image: UIAccessibilityTraits
    The accessibility element behaves like an image.


    static var searchField: UIAccessibilityTraits
    The accessibility element behaves like a search field.


    static var keyboardKey: UIAccessibilityTraits
    The accessibility element behaves like a keyboard key.


    static var staticText: UIAccessibilityTraits
    The accessibility element behaves like static text that can't change.


    static var header: UIAccessibilityTraits
    The accessibility element is a header that divides content into
    sections, such as the title of a navigation bar.


    static var tabBar: UIAccessibilityTraits
    The accessibility element behaves like a tab bar.


    static var summaryElement: UIAccessibilityTraits
    The accessibility element provides summary information when the app starts.


    static var selected: UIAccessibilityTraits
    The accessibility element is currently in a selected state.


    static var notEnabled: UIAccessibilityTraits
    The accessibility element isn't in an enabled state and doesn't respond to
    user interaction.


    static var adjustable: UIAccessibilityTraits
    The accessibility element allows continuous adjustment through a range of
    values.


    static var allowsDirectInteraction: UIAccessibilityTraits
    The accessibility element allows direct touch interaction for VoiceOver users.


    static var updatesFrequently: UIAccessibilityTraits
    The accessibility element frequently updates its label or value.


    static var causesPageTurn: UIAccessibilityTraits
    The accessibility element causes an automatic page turn when VoiceOver
    finishes reading the text within it.


    static var playsSound: UIAccessibilityTraits
    The accessibility element plays its own sound when the user activates it.


    static var startsMediaSession: UIAccessibilityTraits
    The accessibility element starts a media session when the user activates it.
    accessibilityTraits
    https://developer.apple.com/documentation/uikit/uiaccessibility/uiaccessibilitytraits

    View full-size slide

  76. ΞΫηγϏϦςΟͷઃఆΛ֬ೝ͢ΔTips
    79

    View full-size slide

  77. ΞΫηγϏϦςΟͷઃఆΛ֬ೝ͢ΔTips
    • ʮԻ੠ίϯτϩʔϧʯΛ༗ޮʹͯ͠
    ʮΦʔόʔϨΠʯͷઃఆΛʮ߲໨
    ໊ʯʹ͢Δ͜ͱͰઃఆࡁΈͷϥϕϧ
    ͕ՄࢹԽͰ͖Δ


    • VoiceOverΛ࣮ࡍʹ࢖Θͣͱ΋ϥϕ
    ϧͷ֬ೝ͕Ͱ͖ͯศར
    80

    View full-size slide

  78. Accessibility Inspector
    81
    • ΞΫηγϏϦςΟʹؔ͢ΔΑΓৄࡉ
    ͷ৘ใΛௐ΂Δʹ͸Xcodeʹ෇ଐͷ
    Accessibility InspectorΛར

    View full-size slide

  79. ΞΫηγϏϦςΟʹؔ͢Δςετ
    • SnapshotTesting

    https://github.com/pointfreeco/swift-snapshot-testing


    • AccessibilitySnapshot

    https://github.com/cashapp/AccessibilitySnapshot


    • A11yUITests

    https://github.com/rwapp/A11yUITests
    82

    View full-size slide

  80. ར༻ऀͷ੠
    • “ૉఢͳॻ͖खͱܨ͕Δ৔ॴͰ͋Γɺࢲࣗ਎͕ࣗ෼Λදݱ͢Δ৔ॴͰ͋ΔnoteͰ
    ͜Ε͔Β΋ॻ͍͍͖͍ͯͨɻͦͷҰาͱͯ͠ΞΫηγϏϦςΟͷվળ͸େ͖ͳྗ
    ʹͳͬͨɻ”

    https://note.com/azusa_1202/n/neeeb0112c395


    • “note͕ΞΫηγϏϦςΟʹऔΓ૊Ή͜ͱʹΑΓɺࢲͷΑ͏ͳεΫϦʔϯɾϦʔ
    μʔ࢖༻ऀ͚ͩͰͳ͘ɺ਎ମ΍ೝ஌ʹࠔ೉͞Λײ͡Δਓ΍ಡࣈো֐΍ॻࣈো֐ͳ
    Ͳɺ͍Ζ͍Ζͳ“ଟ༷ੑ”ʹԠ͑ΒΕΔαʔϏεʹ͍͍ۙͮͯ͘ͱࢥ͍·͢ɻ”

    https://note.com/kokoa_nattsu/n/nc08f81cda989
    83

    View full-size slide

  81. ࠓޙͷల๬
    • ͷ΢ΣϒɾΞϓϦͷΞΫηγϏϦςΟͷ޲্׆ಈ͸·ͩ·ͩ࢝
    ·ͬͨ͹͔ΓͰෆ׬શ


    • ΞΫηγϏϦςΟͷ޲্ʹΰʔϧ͸ͳ͍


    • ఆৗͷ։ൃɾ඼࣭؅ཧͷதʹ૊ΈࠐΜͰ։ൃΛਐΊͯߦ͖͍ͨ


    • ఆظతʹϢʔβʔΠϯλϏϡʔ΍ΞϯέʔτͳͲΛ࣮ࢪͯ͠ར༻ऀͷ੠Λ
    र্͍͍͛ͯ͘࢓૊Έͮ͘Γ
    84

    View full-size slide

  82. ·ͱΊ
    • ΞΫηγϏϦςΟΛ޲্͢Δͱҙؾࠐ·ͣʹόάΛमਖ਼͢Δͱ͍͏ҙࣝ


    • NSObject͕͍࣋ͬͯΔaccessibilityؔ࿈ϓϩύςΟ͔Β


    • ༻్ʹ߹ΘͤͯUIAccessibility΍Accessibility Frameworkͷ
    ػೳΛར༻ͯ͠ମݧΛ޲্Λ͍͚ͤͯ͞͹Αͦ͞͏


    • ΞΫηγϒϧͳΞϓϦ͕૿͑Δ͜ͱͰɺࠔ͍ͬͯΔਓ͕গ͠Ͱ΋ݮΕ͹
    85

    View full-size slide

  83. ৘ใऩू
    • https://developer.apple.com/accessibility/


    • https://a11yj.herokuapp.com

    #apple-app νϟϯωϧͰAppleϓϥοτϑΥʔϜʹؔ͢Δ৘ใަ׵


    • https://a11y-guidelines.orange.com/en/


    • https://accrefs.jp

    Webʹؔ͢Δ৘ใ͕΄ͱΜͲ͚ͩͲΞϓϦ΋গ͠
    86

    View full-size slide

  84. ͰͷऔΓ૊Έ
    • ΞΫηγϏϦςΟڧԽͷҰ؀ͰɺεΫϦʔϯϦʔμʔʹΑΔಡΈ্͛ͷΧ
    ΠθϯΛߦ͍·ͨ͠

    https://note.com/info/n/n4dbe29c380a1


    • VoiceOverɺDynamic TypeͳͲɺiOSΞϓϦͷΞΫηγϏϦςΟػೳ
    ͷڧԽΛߦ͍·ͨ͠

    https://note.com/info/n/nfcc92e285d96


    • ͳͲͳͲ
    87

    View full-size slide

  85. αϯϓϧίʔυ
    88
    https://github.com/fromkk/AccessibilitySampler

    View full-size slide