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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

  8. Types of disabilities
    Hearing Motor Skills
    Learning
    Vision

    View full-size slide

  9. Hearing Motor Skills
    Learning
    Vision

    View full-size slide

  10. Hearing Motor Skills
    Learning
    Vision

    View full-size slide

  11. Hearing Motor Skills
    Learning
    Vision

    View full-size slide

  12. Hearing Motor Skills
    Learning
    Vision

    View full-size slide

  13. Hearing Motor Skills
    Learning
    Vision

    View full-size slide

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

    View full-size slide

  15. Interacting with Voice Over
    • Gestures

    • Switch Control

    View full-size slide

  16. Voice Over may already
    work in you app

    View full-size slide

  17. Voice Over:
    Step by Step Guide

    View full-size slide

  18. Unlock your
    iPhone

    View full-size slide

  19. Tap on
    element

    View full-size slide

  20. Navigation:
    swipe

    View full-size slide

  21. Turn page:
    3 fingers swipe

    View full-size slide

  22. Action:
    Double Tap

    View full-size slide

  23. 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 full-size slide

  24. Hearing Motor Skills
    Learning
    Vision

    View full-size slide

  25. How can I make my
    app accessible?

    View full-size slide

  26. Testing & Audit

    View full-size slide

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

    • Inspector Audit

    • Switch off display brightness and check flow

    View full-size slide

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

    • Inspector Audit

    • Switch off display brightness and check flow

    View full-size slide

  29. Inspector Audit

    View full-size slide

  30. Inspector Audit

    View full-size slide

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

    • Inspector Audit

    • Switch off display brightness and check flow

    View full-size slide

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

    • Inspector Audit

    • Switch off display brightness and check flow

    View full-size slide

  33. Demo with
    BabyOwl app

    View full-size slide

  34. Accessibility Audit Results
    • Cell description

    • Interacting with rating

    • Button description

    • Speed, strength, endurance

    • Recommendation

    • Slider

    View full-size slide

  35. Let’s make it
    accessible!

    View full-size slide

  36. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View full-size slide

  37. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View full-size slide

  38. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View full-size slide

  39. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View full-size slide

  40. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View full-size slide

  41. Do I serve a purpose?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  44. What is my name?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  48. What’s my personality?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  51. What’s my value?

    View full-size slide

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

    View full-size slide

  53. What will be the
    result of action?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  56. Where I am?
    open var accessibilityFrame: CGRect

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  60. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View full-size slide

  61. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View full-size slide

  62. 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 full-size slide

  63. 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 full-size slide

  64. 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 full-size slide

  65. 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 full-size slide

  66. 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 full-size slide

  67. 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 full-size slide

  68. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View full-size slide

  69. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View full-size slide

  70. @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 full-size slide

  71. 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 full-size slide

  72. 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 full-size slide

  73. 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 full-size slide

  74. 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 full-size slide

  75. 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 full-size slide

  76. 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 full-size slide

  77. 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 full-size slide

  78. 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 full-size slide

  79. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View full-size slide

  80. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View full-size slide

  81. 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 full-size slide

  82. 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 full-size slide

  83. 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 full-size slide

  84. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View full-size slide

  85. Bugs
    • Button description

    • Interacting with rating

    • Context for labels

    • Core Graphics elements

    View full-size slide

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

    View full-size slide

  87. 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 full-size slide

  88. That’s all.
    Not that difficult.

    View full-size slide

  89. • Transparency and blurring

    • Contrast

    • Sizing

    • Motion

    • Drang & Drop
    Some more features
    we can support

    View full-size slide

  90. • Transparency and blurring

    • Contrast

    • Sizing

    • Motion

    • Drang & Drop
    Some more features
    we can support

    View full-size slide

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

    View full-size slide

  92. 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 full-size slide

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

    • Contrast

    • Sizing

    • Motion

    • Drag & Drop

    View full-size slide

  94. Minimum Contrast Ratio

    View full-size slide

  95. • Minimum contrast ratio should be 4.5 : 1

    • According to WGAG 2.0
    Minimum Contrast Ratio

    View full-size slide

  96. Minimum Contrast Ratio

    View full-size slide

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

    View full-size slide

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

    • Contrast

    • Sizing

    • Motion

    • Drag & Drop

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  101. But don’t forget about Layout

    View full-size slide

  102. • Transparency and blurring

    • Contrast

    • Sizing

    • Motion

    • Drang & Drop
    Some more features
    we can support

    View full-size slide

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

    View full-size slide

  104. • Transparency and blurring

    • Contrast

    • Sizing

    • Motion

    • Drag & Drop
    Some more features
    we can support

    View full-size slide

  105. 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 full-size slide

  106. • Simplicity

    • Use Siri

    • Use Siri Shortcuts
    Few more words
    about accessibility

    View full-size slide

  107. • Simplicity

    • Use Siri

    • Use Siri Shortcuts
    Few more words
    about accessibility

    View full-size slide

  108. • Simplicity

    • Use Siri

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

    View full-size slide

  109. • 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 full-size slide

  110. • 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 full-size slide

  111. • 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 full-size slide

  112. • 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 full-size slide

  113. 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 full-size slide