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

Making your iOS application accessible

Making your iOS application accessible

Author: Nikita Kirichek ( https://www.facebook.com/nikita.kirichek )

Доклад включает в себя:
- Как быстро сделать существующее приложение для iOS доступным для всех.
- Тестированию приложений на соответствие Apple Accessibility Guidelines
- Инструменты UIKit для обеспечения accessibility.
- Короткая демонстрация того, как всего несколько строк кода предоставят возможность использования вашего приложения для людей с инвалидностью

This talk was made for CocoaHeads Kyiv #14 which took place Oct 6 2018.

CocoaHeads Ukraine

October 06, 2018
Tweet

More Decks by CocoaHeads Ukraine

Other Decks in Programming

Transcript

  1. Making your iOS
    application accessible
    Mykyta Kyrychek, Software developer

    View Slide

  2. Agenda
    • Common accessibility issues of mobile apps

    • How can these issues be solved?

    • Auditing the app to detect any accessibility issues

    • Using the Accessibility API to enhance user experience for
    a person with disability

    View Slide

  3. Agenda
    • Common accessibility issues of mobile apps

    • How can these issues be solved?

    • Auditing the app to detect any accessibility issues

    • Using the Accessibility API to enhance user experience for
    a person with disability

    View Slide

  4. Agenda
    • Common accessibility issues of mobile apps

    • How can these issues be solved?

    • Auditing the app to detect any accessibility issues

    • Using the Accessibility API to enhance user experience for
    a person with disability

    View Slide

  5. Agenda
    • Common accessibility issues of mobile apps

    • How can these issues be solved?

    • Auditing the app to detect any accessibility issues

    • Using the Accessibility API to enhance user experience for
    a person with disability

    View Slide

  6. Agenda
    • Common accessibility issues of mobile apps

    • How can these issues be solved?

    • Auditing the app to detect any accessibility issues

    • Using the Accessibility API to enhance user experience for
    a person with disability

    View Slide

  7. What issues people with disabilities
    may face when they use our apps?

    View Slide

  8. Types of disabilities
    Hearing Motor Skills
    Learning
    Vision

    View Slide

  9. Hearing Motor Skills
    Learning
    Vision

    View Slide

  10. View Slide

  11. Hearing Motor Skills
    Learning
    Vision

    View Slide

  12. Hearing Motor Skills
    Learning
    Vision

    View Slide

  13. Hearing Motor Skills
    Learning
    Vision

    View Slide

  14. View Slide

  15. Hearing Motor Skills
    Learning
    Vision

    View Slide

  16. 1 of every 7 people on Earth
    has a disability

    View Slide

  17. Solution

    View Slide

  18. Voice Over

    View Slide

  19. Interacting with Voice Over
    • Gestures

    • Switch Control

    View Slide

  20. View Slide

  21. Voice Over may already
    work in you app

    View Slide

  22. Voice Over:
    Step by Step Guide

    View Slide

  23. Unlock your
    iPhone

    View Slide

  24. Tap on
    element

    View Slide

  25. Navigation:
    swipe

    View Slide

  26. Turn page:
    3 fingers swipe

    View Slide

  27. Action:
    Double Tap

    View Slide

  28. 1. Search, heading

    2. App Store, search field, double
    tap to edit

    3. Trending, heading

    4. merge plane, button, double tap
    to open

    5. …

    6. whatsapp, button, double tap to
    open

    7. Today, tab, 1 of 5

    8. …

    9. Selected, search, 5 of 5

    View Slide

  29. Hearing Motor Skills
    Learning
    Vision

    View Slide

  30. How can I make my
    app accessible?

    View Slide

  31. Testing & Audit

    View Slide

  32. Testing & Audit
    • Turn voice over on and try to use your app

    • Inspector Audit

    • Switch off display brightness and check flow

    View Slide

  33. View Slide

  34. View Slide

  35. View Slide

  36. View Slide

  37. Testing & Audit
    • Turn voice over on and try to use your app

    • Inspector Audit

    • Switch off display brightness and check flow

    View Slide

  38. Inspector Audit

    View Slide

  39. Inspector Audit

    View Slide

  40. View Slide

  41. Testing & Audit
    • Turn voice over on and try to use your app

    • Inspector Audit

    • Switch off display brightness and check flow

    View Slide

  42. Testing & Audit
    • Turn voice over on and try to use your app

    • Inspector Audit

    • Switch off display brightness and check flow

    View Slide

  43. Demo with
    BabyOwl app

    View Slide

  44. Accessibility Audit Results
    • Cell description

    • Interacting with rating

    • Button description

    • Speed, strength, endurance

    • Recommendation

    • Slider

    View Slide

  45. View Slide

  46. Let’s make it
    accessible!

    View Slide

  47. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View Slide

  48. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View Slide

  49. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View Slide

  50. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View Slide

  51. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View Slide

  52. Do I serve a purpose?

    View Slide

  53. Do I serve a purpose?
    open var isAccessibilityElement: Bool

    View Slide

  54. Do I serve a purpose?
    open var isAccessibilityElement: Bool
    isAccessibilityElement = true

    View Slide

  55. What is my name?

    View Slide

  56. What is my name?
    open var accessibilityLabel: String?

    View Slide

  57. What is my name?
    open var accessibilityLabel: String?
    accessibilityLabel = "Love"

    View Slide

  58. What is my name?
    open var accessibilityLabel: String?
    accessibilityLabel = "Love"
    accessibilityLabel = “Love Button”

    View Slide

  59. What’s my personality?

    View Slide

  60. What’s my personality?
    open var accessibilityTraits: UIAccessibilityTraits

    View Slide

  61. What’s my personality?
    open var accessibilityTraits: UIAccessibilityTraits
    accessibilityTraits = .button

    View Slide

  62. What’s my value?

    View Slide

  63. What’s my value?
    open var accessibilityValue: String?

    View Slide

  64. What will be the
    result of action?

    View Slide

  65. open var accessibilityHint: String?
    What will be the
    result of action?

    View Slide

  66. open var accessibilityHint: String?
    accessibilityHint = "Loves displayed owl"
    What will be the
    result of action?

    View Slide

  67. Where I am?
    open var accessibilityFrame: CGRect

    View Slide

  68. barButton?.isAccessibilityElement = true
    barButton?.accessibilityLabel = loved ? "Dislike" : “Love"
    barButton?.accessibilityTraits = .button
    barButton?.accessibilityHint = loved ? "Dislikes current owl" :
    "Loves current owl"

    View Slide

  69. barButton?.isAccessibilityElement = true
    barButton?.accessibilityLabel = loved ? "Dislike" : “Love"
    barButton?.accessibilityTraits = .button
    barButton?.accessibilityHint = loved ? "Dislikes current owl" :
    "Loves current owl"

    View Slide

  70. barButton?.accessibilityLabel = loved ? "Dislike" : “Love"
    barButton?.accessibilityHint = loved ? "Dislikes current owl" :
    "Loves current owl"

    View Slide

  71. View Slide

  72. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View Slide

  73. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View Slide

  74. View Slide

  75. isAccessibilityElement = true
    accessibilityLabel = "Cute rating"
    accessibilityHint = "Rates from 1 to 5"
    accessibilityTraits = .adjustable
    override var accessibilityValue: String? {
    set {}
    get { return "\(currentPinguis) pingui" }
    }
    override func accessibilityIncrement() {
    currentPinguis += 1
    }
    override func accessibilityDecrement() {
    currentPinguis -= 1
    }

    View Slide

  76. isAccessibilityElement = true
    accessibilityLabel = "Cute rating"
    accessibilityHint = "Rates from 1 to 5"
    accessibilityTraits = .adjustable
    override var accessibilityValue: String? {
    set {}
    get { return "\(currentPinguis) pingui" }
    }
    override func accessibilityIncrement() {
    currentPinguis += 1
    }
    override func accessibilityDecrement() {
    currentPinguis -= 1
    }

    View Slide

  77. isAccessibilityElement = true
    accessibilityLabel = "Cute rating"
    accessibilityHint = "Rates from 1 to 5"
    accessibilityTraits = .adjustable
    override var accessibilityValue: String? {
    set {}
    get { return "\(currentPinguis) pingui" }
    }
    override func accessibilityIncrement() {
    currentPinguis += 1
    }
    override func accessibilityDecrement() {
    currentPinguis -= 1
    }

    View Slide

  78. isAccessibilityElement = true
    accessibilityLabel = "Cute rating"
    accessibilityHint = "Rates from 1 to 5"
    accessibilityTraits = .adjustable
    override var accessibilityValue: String? {
    set {}
    get { return "\(currentPinguis) pingui" }
    }
    override func accessibilityIncrement() {
    currentPinguis += 1
    }
    override func accessibilityDecrement() {
    currentPinguis -= 1
    }

    View Slide

  79. isAccessibilityElement = true
    accessibilityLabel = "Cute rating"
    accessibilityHint = "Rates from 1 to 5"
    accessibilityTraits = .adjustable
    override var accessibilityValue: String? {
    set {}
    get { return "\(currentPinguis) pingui" }
    }
    override func accessibilityIncrement() {
    currentPinguis += 1
    }
    override func accessibilityDecrement() {
    currentPinguis -= 1
    }

    View Slide

  80. isAccessibilityElement = true
    accessibilityLabel = "Cute rating"
    accessibilityHint = "Rates from 1 to 5"
    accessibilityTraits = .adjustable
    override var accessibilityValue: String? {
    set {}
    get { return "\(currentPinguis) pingui" }
    }
    override func accessibilityIncrement() {
    currentPinguis += 1
    }
    override func accessibilityDecrement() {
    currentPinguis -= 1
    }

    View Slide

  81. View Slide

  82. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View Slide

  83. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View Slide

  84. @available(iOS 3.0, *)
    open class UIAccessibilityElement : NSObject {
    public init(accessibilityContainer container: Any)
    unowned(unsafe) open var accessibilityContainer: AnyObject?
    open var isAccessibilityElement: Bool
    open var accessibilityLabel: String?
    open var accessibilityHint: String?
    open var accessibilityValue: String?
    open var accessibilityFrame: CGRect
    open var accessibilityTraits: UIAccessibilityTraits
    }

    View Slide

  85. let acessibiltyElement = UIAccessibilityElement(accessibilityContainer: view)
    acessibiltyElement.accessibilityLabel = text
    let frame = leftView.frame.union(rightView.frame)
    let screenFrame = UIAccessibility.convertToScreenCoordinates(frame, in: view)
    acessibiltyElement.accessibilityFrame = screenFrame
    acessibiltyElement.accessibilityTraits = .staticText
    view.accessibilityElements?.append(acessibiltyElement)

    View Slide

  86. let acessibiltyElement = UIAccessibilityElement(accessibilityContainer: view)
    acessibiltyElement.accessibilityLabel = text
    let frame = leftView.frame.union(rightView.frame)
    let screenFrame = UIAccessibility.convertToScreenCoordinates(frame, in: view)
    acessibiltyElement.accessibilityFrame = screenFrame
    acessibiltyElement.accessibilityTraits = .staticText
    view.accessibilityElements?.append(acessibiltyElement)

    View Slide

  87. let acessibiltyElement = UIAccessibilityElement(accessibilityContainer: view)
    acessibiltyElement.accessibilityLabel = text
    let frame = leftView.frame.union(rightView.frame)
    let screenFrame = UIAccessibility.convertToScreenCoordinates(frame, in: view)
    acessibiltyElement.accessibilityFrame = screenFrame
    acessibiltyElement.accessibilityTraits = .staticText
    view.accessibilityElements?.append(acessibiltyElement)

    View Slide

  88. let acessibiltyElement = UIAccessibilityElement(accessibilityContainer: view)
    acessibiltyElement.accessibilityLabel = text
    let frame = leftView.frame.union(rightView.frame)
    let screenFrame = UIAccessibility.convertToScreenCoordinates(frame, in: view)
    acessibiltyElement.accessibilityFrame = screenFrame
    acessibiltyElement.accessibilityTraits = .staticText
    view.accessibilityElements?.append(acessibiltyElement)

    View Slide

  89. let acessibiltyElement = UIAccessibilityElement(accessibilityContainer: view)
    acessibiltyElement.accessibilityLabel = text
    let frame = leftView.frame.union(rightView.frame)
    let screenFrame = UIAccessibility.convertToScreenCoordinates(frame, in: view)
    acessibiltyElement.accessibilityFrame = screenFrame
    acessibiltyElement.accessibilityTraits = .staticText
    view.accessibilityElements?.append(acessibiltyElement)

    View Slide

  90. let acessibiltyElement = UIAccessibilityElement(accessibilityContainer: view)
    acessibiltyElement.accessibilityLabel = text
    let frame = leftView.frame.union(rightView.frame)
    let screenFrame = UIAccessibility.convertToScreenCoordinates(frame, in: view)
    acessibiltyElement.accessibilityFrame = screenFrame
    acessibiltyElement.accessibilityTraits = .staticText
    view.accessibilityElements?.append(acessibiltyElement)

    View Slide

  91. let acessibiltyElement = UIAccessibilityElement(accessibilityContainer: view)
    acessibiltyElement.accessibilityLabel = text
    let frame = leftView.frame.union(rightView.frame)
    let screenFrame = UIAccessibility.convertToScreenCoordinates(frame, in: view)
    acessibiltyElement.accessibilityFrame = screenFrame
    acessibiltyElement.accessibilityTraits = .staticText
    view.accessibilityElements?.append(acessibiltyElement)

    View Slide

  92. let acessibiltyElement = UIAccessibilityElement(accessibilityContainer: view)
    acessibiltyElement.accessibilityLabel = text
    let frame = leftView.frame.union(rightView.frame)
    let screenFrame = UIAccessibility.convertToScreenCoordinates(frame, in: view)
    acessibiltyElement.accessibilityFrame = screenFrame
    acessibiltyElement.accessibilityTraits = .staticText
    view.accessibilityElements?.append(acessibiltyElement)

    View Slide

  93. View Slide

  94. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View Slide

  95. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View Slide

  96. extension GraphView {
    override func draw(_ rect: CGRect) {
    addLine(from: startSpeedPoint, to: endSpeedPoint)
    addLine(from: startEdurancePoint, to: endEdurancePoint)
    addLine(from: startStrengthPoint, to: endStrengthPoint)
    let speedElement = accessibilityElement(startPoint: startSpeedPoint,
    endPoint: endSpeedPoint,
    text: "Speed \(speed) precents")
    let enduranceElement = accessibilityElement(startPoint: startEdurancePoint,
    endPoint: endEdurancePoint,
    text: "Endurance \(endurance) percents")
    let strengthElement = accessibilityElement(startPoint: startStrengthPoint,
    endPoint: endStrengthPoint,
    text: "Strength \(strength) percents")
    accessibilityElements = [speedElement, enduranceElement, strengthElement]
    }
    }

    View Slide

  97. extension GraphView {
    override func draw(_ rect: CGRect) {
    addLine(from: startSpeedPoint, to: endSpeedPoint)
    addLine(from: startEdurancePoint, to: endEdurancePoint)
    addLine(from: startStrengthPoint, to: endStrengthPoint)
    let speedElement = accessibilityElement(startPoint: startSpeedPoint,
    endPoint: endSpeedPoint,
    text: "Speed \(speed) precents")
    let enduranceElement = accessibilityElement(startPoint: startEdurancePoint,
    endPoint: endEdurancePoint,
    text: "Endurance \(endurance) percents")
    let strengthElement = accessibilityElement(startPoint: startStrengthPoint,
    endPoint: endStrengthPoint,
    text: "Strength \(strength) percents")
    accessibilityElements = [speedElement, enduranceElement, strengthElement]
    }
    }

    View Slide

  98. extension GraphView {
    override func draw(_ rect: CGRect) {
    addLine(from: startSpeedPoint, to: endSpeedPoint)
    addLine(from: startEdurancePoint, to: endEdurancePoint)
    addLine(from: startStrengthPoint, to: endStrengthPoint)
    let speedElement = accessibilityElement(startPoint: startSpeedPoint,
    endPoint: endSpeedPoint,
    text: "Speed \(speed) precents")
    let enduranceElement = accessibilityElement(startPoint: startSpeedPoint,
    endPoint: endSpeedPoint,
    text: "Speed \(speed) precents")
    let strengthElement = accessibilityElement(startPoint: startSpeedPoint,
    endPoint: endSpeedPoint,
    text: "Speed \(speed) precents")
    accessibilityElements = [speedElement, enduranceElement, strengthElement]
    }
    }

    View Slide

  99. View Slide

  100. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View Slide

  101. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View Slide

  102. Send notification
    UIAccessibility.post(notification: .announcement,
    argument: "Loved")
    UIAccessibility.post(notification: .screenChanged,
    argument: "New recommendation owl")

    View Slide

  103. View Slide

  104. extension UIAccessibilityElement {
    open var isAccessibilityElement: Bool
    open var accessibilityLabel: String?
    open var accessibilityHint: String?
    open var accessibilityValue: String?
    open var accessibilityTraits: UIAccessibilityTraits
    }
    UIAccessibility Basic API

    View Slide

  105. That’s all.
    Not that difficult.

    View Slide

  106. View Slide

  107. • Transparency and blurring

    • Contrast

    • Sizing

    • Motion

    • Drang & Drop
    Some more features
    we can support

    View Slide

  108. • Transparency and blurring

    • Contrast

    • Sizing

    • Motion

    • Drang & Drop
    Some more features
    we can support

    View Slide

  109. View Slide

  110. extension UIAccessibilityElement {
    }
    public static var isReduceTransparencyEnabled: Bool { get }
    Transparency and blurring

    View Slide

  111. View Slide

  112. if !UIAccessibility.isReduceTransparencyEnabled {
    let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.dark)
    let blurEffectView = UIVisualEffectView(effect: blurEffect)
    blurEffectView.contentView.addSubview(photosRollView)
    rollContainerView = blurEffectView
    } else {
    rollContainerView = UIView()
    rollContainerView.addSubview(photosRollView)
    }

    View Slide

  113. Some more features
    we can support
    • Transparency and blurring

    • Contrast

    • Sizing

    • Motion

    • Drag & Drop

    View Slide

  114. Contrast

    View Slide

  115. Contrast

    View Slide

  116. Contrast

    View Slide

  117. Contrast

    View Slide

  118. Contrast

    View Slide

  119. Contrast

    View Slide

  120. Contrast

    View Slide

  121. Minimum Contrast Ratio

    View Slide

  122. • Minimum contrast ratio should be 4.5 : 1

    • According to WGAG 2.0
    Minimum Contrast Ratio

    View Slide

  123. Minimum Contrast Ratio

    View Slide

  124. View Slide

  125. extension UIAccessibilityElement {
    }
    Darken Colors
    public static var isDarkerSystemColorsEnabled: Bool { get }

    View Slide

  126. Few more features
    we can support
    • Transparency and blurring

    • Contrast

    • Sizing

    • Motion

    • Drag & Drop

    View Slide

  127. View Slide

  128. View Slide

  129. View Slide

  130. View Slide

  131. ageLabel.font = UIFont.preferredFont(forTextStyle: .body)
    ageLabel.adjustsFontForContentSizeCategory = true
    Sizing

    View Slide

  132. ageLabel.font = UIFont.preferredFont(forTextStyle: .body)
    ageLabel.adjustsFontForContentSizeCategory = true
    ageLabel.font = UIFontMetrics.default.scaledFont(for: customFont)
    Custom Font

    View Slide

  133. But don’t forget about Layout

    View Slide

  134. • Transparency and blurring

    • Contrast

    • Sizing

    • Motion

    • Drang & Drop
    Some more features
    we can support

    View Slide

  135. extension UIAccessibilityElement {
    }
    Motion
    public static var isReduceMotionEnabled: Bool { get }

    View Slide

  136. View Slide

  137. • Transparency and blurring

    • Contrast

    • Sizing

    • Motion

    • Drag & Drop
    Some more features
    we can support

    View Slide

  138. Drag & Drop
    open class UIAccessibilityLocationDescriptor : NSObject {
    public convenience init(name: String, view: UIView)
    public convenience init(name: String, point: CGPoint, in view: UIView)
    public init(attributedName: NSAttributedString, point: CGPoint, in view: UIView)
    weak open var view: UIView? { get }
    open var point: CGPoint { get }
    open var name: String { get }
    open var attributedName: NSAttributedString { get }
    }

    View Slide

  139. • Simplicity

    • Use Siri

    • Use Siri Shortcuts
    Few more words
    about accessibility

    View Slide

  140. • Simplicity

    • Use Siri

    • Use Siri Shortcuts
    Few more words
    about accessibility

    View Slide

  141. • Simplicity

    • Use Siri

    • Use Siri Shortcuts (@available iOS 12)
    Few more words
    about accessibility

    View Slide

  142. • Easier to write integration tests with KIF
    or UI Testing frameworks

    • Increase user base

    • It helps you address accessibility guidelines
    (508 compliance)

    • It’s the right thing to do
    Why Accessibility?

    View Slide

  143. • Easier to write integration tests with KIT
    or UI Testing frameworks

    • Increase user base

    • It helps you address accessibility guidelines
    (508 compliance)

    • It’s the right thing to do
    Why Accessibility?

    View Slide

  144. • Easier to write integration tests with KIT
    or UI Testing frameworks

    • Increase user base

    • It helps you address accessibility guidelines
    (508 compliance)

    • It’s the right thing to do
    Why Accessibility?

    View Slide

  145. • Easier to write integration tests with KIT
    or UI Testing frameworks

    • Increase user base

    • It helps you address accessibility guidelines
    (508 compliance)

    • It’s the right thing to do
    Why Accessibility?

    View Slide

  146. View Slide

  147. Thanks!
    https://github.com/nikitakirichek
    https://www.facebook.com/nikita.kirichek
    Convenience for you is independence for me: https://developer.apple.com/videos/play/wwdc2017/110/
    Deliver an Exceptional Accessibility Experience: https://developer.apple.com/videos/play/wwdc2018/230/

    View Slide