Skip to content

Commit

Permalink
fix(Accessibility): improve Accessibility on Cards. Update Cards docs (
Browse files Browse the repository at this point in the history
…#86)

* set accesibilityElement to Card when it has no buttons

* remove white line

* Run swiftformat

* improve Cards accessibility

* override traits to cards

* swiftformat

* Run swiftformat

* IOS-6513 Update components list of main README

* IOS-6513 Move HighlightedCard to Components/Cards folder and update README of cards component.

* IOS-6513 Improve linking to cards components

* Apply suggestions from code review

Co-authored-by: Julian <julian.alonsocarballo@telefonica.com>

* set accesibilityElement to Card when it has no buttons

* remove white line

* improve Cards accessibility

* Run swiftformat

* override traits to cards

* swiftformat

* Improve accessibility on cards.

* update cards documentation

* add accessibility docs to Cards docs

Co-authored-by: Jose Miguel Brocal Gallego <jmbrocal@tuenti.com>
  • Loading branch information
Julian and jmbrocal authored Feb 23, 2021
1 parent a3a0054 commit 3bbe36b
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 1 deletion.
29 changes: 29 additions & 0 deletions Mistica/Source/Components/Cards/DataCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public class DataCard: UIView {
static let largeIconSize = CGFloat(24)
}

private lazy var cardAccessibilityElement = UIAccessibilityElement(accessibilityContainer: self)
private let iconContainerView = UIView()
private var iconImageView = DataCardAsset()
private let cardBaseView = CardBase()
Expand All @@ -83,6 +84,18 @@ public class DataCard: UIView {
}
}

override public var accessibilityElements: [Any]? {
get {
// We must set the frame and be sure it is already calculated.
cardAccessibilityElement.accessibilityFrameInContainerSpace = bounds
return [
cardAccessibilityElement,
cardBaseView
].compactMap { $0 }
}
set {}
}

override public init(frame: CGRect) {
super.init(frame: frame)
commomInit()
Expand Down Expand Up @@ -122,6 +135,15 @@ public extension DataCard {
var linkButton: Button {
cardBaseView.buttonsView.linkButton
}

override var accessibilityTraits: UIAccessibilityTraits {
get {
cardAccessibilityElement.accessibilityTraits
}
set {
cardAccessibilityElement.accessibilityTraits = newValue
}
}
}

// MARK: Private
Expand Down Expand Up @@ -199,6 +221,13 @@ private extension DataCard {
case .primaryAndLink(let primaryButton, let linkButton):
cardBaseView.configureButtons(primaryButton: primaryButton, linkButton: linkButton)
}

cardAccessibilityElement.accessibilityLabel = [
configuration.headline,
configuration.title,
configuration.subtitle,
configuration.descriptionTitle
].compactMap { $0 }.joined(separator: " ")
}
}

Expand Down
31 changes: 31 additions & 0 deletions Mistica/Source/Components/Cards/HighlightedCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class HighlightedCard: UIView {
case secondary
}

private lazy var cardAccessibilityElement = UIAccessibilityElement(accessibilityContainer: self)
private lazy var verticalStackView = UIStackView()
private lazy var horizontalStackView = UIStackView()

Expand Down Expand Up @@ -140,6 +141,20 @@ public class HighlightedCard: UIView {
}
}

override public var accessibilityElements: [Any]? {
get {
updateAccessibilityLabel()
// We must set the frame and be sure it is already calculated.
cardAccessibilityElement.accessibilityFrameInContainerSpace = bounds
return [
cardAccessibilityElement,
actionButton.isHidden ? nil : actionButton,
closeButton.isHidden ? nil : closeButton
].compactMap { $0 }
}
set {}
}

public init(title: String? = nil,
subtitle: String? = nil,
rightImage: UIImage? = nil,
Expand Down Expand Up @@ -292,6 +307,15 @@ public extension HighlightedCard {
backgroundImageView.accessibilityIdentifier = newValue
}
}

override var accessibilityTraits: UIAccessibilityTraits {
get {
cardAccessibilityElement.accessibilityTraits
}
set {
cardAccessibilityElement.accessibilityTraits = newValue
}
}
}

// MARK: Private
Expand Down Expand Up @@ -443,4 +467,11 @@ private extension HighlightedCard {
@objc func actionButtonTapped() {
actionButtonCallback?()
}

func updateAccessibilityLabel() {
cardAccessibilityElement.accessibilityLabel = [
titleAccessibilityLabel,
subtitleAccessibilityLabel
].compactMap { $0 }.joined(separator: " ")
}
}
12 changes: 11 additions & 1 deletion Mistica/Source/Components/Cards/Internals/CardBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,17 @@ extension CardBase {
contentView.descriptionTitle = newValue
}
}


override var accessibilityElements: [Any]? {
get {
[
buttonsView,
fragmentView as Any
].compactMap { $0 }
}
set { }
}

func configureButtons(primaryButton: CardButton?, linkButton: CardLinkButton?) {
buttonsView.configureButtons(primaryButton: primaryButton, linkButton: linkButton)

Expand Down
10 changes: 10 additions & 0 deletions Mistica/Source/Components/Cards/Internals/CardButtonsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ class CardButtons: UIStackView {
private var primaryActionHandler: (() -> Void)?
private var linkActionHandler: (() -> Void)?

override var accessibilityElements: [Any]? {
get {
[
primaryButton.superview == nil ? nil : primaryButton,
linkButton.superview == nil ? nil : linkButton
].compactMap { $0 }
}
set {}
}

override public init(frame: CGRect) {
super.init(frame: frame)
commomInit()
Expand Down
29 changes: 29 additions & 0 deletions Mistica/Source/Components/Cards/MediaCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class MediaCard: UIView {
static let spacingAfterRichMediaView = CGFloat(8)
}

private lazy var cardAccessibilityElement = UIAccessibilityElement(accessibilityContainer: self)
private var richMediaContainerView = UIView()
private let baseCardView = CardBase()

Expand All @@ -56,6 +57,18 @@ public class MediaCard: UIView {
baseCardView.fragmentView = fragmentView
}
}

override public var accessibilityElements: [Any]? {
get {
cardAccessibilityElement.accessibilityFrameInContainerSpace = bounds
return [
cardAccessibilityElement,
fragmentView as Any,
baseCardView.buttonsView
].compactMap { $0 }
}
set {}
}

public var contentConfiguration: MediaCardConfiguration? {
didSet {
Expand Down Expand Up @@ -93,6 +106,15 @@ public extension MediaCard {
var linkButton: Button {
baseCardView.buttonsView.linkButton
}

override var accessibilityTraits: UIAccessibilityTraits {
get {
cardAccessibilityElement.accessibilityTraits
}
set {
cardAccessibilityElement.accessibilityTraits = newValue
}
}
}

// MARK: Private
Expand Down Expand Up @@ -160,6 +182,13 @@ private extension MediaCard {
baseCardView.descriptionTitle = configuration.descriptionTitle

baseCardView.configureButtons(primaryButton: configuration.button, linkButton: configuration.link)

cardAccessibilityElement.accessibilityLabel = [
baseCardView.headline,
baseCardView.title,
baseCardView.subtitle,
baseCardView.descriptionTitle
].compactMap { $0 }.joined(separator: " ")
}
}

Expand Down
57 changes: 57 additions & 0 deletions Mistica/Source/Components/Cards/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* [HighlightedCard](#highlightedcard)
* [Right Image](#right-image)
* [How to use a HighlightedCard](#how-to-use-a-highlightedcard)
* [Accessibility](#accessibility)

## DataCard

Expand Down Expand Up @@ -112,3 +113,59 @@ highlightedCard.showCloseButton = true
```

When using with autolayout, **HighlightedCard** has no intrinsic size for the width but it has an specific intrinsic size for the height.

# Accessibility

Cards are ready for VoiceOver.
They will be an unique element and if has buttons, them will be also focusable with VoiceOver.

VoiceOver will read the following components (in this particular order):
- headline
- title
- subtitle
- description

## Extra content Accessibility

If extra view is provided to the cell, extra view is reponsible about his accessibility and will be a focusable element for voice over inside the card like the buttons.
To provide a good Accessibility Experience watch [this video](https://developer.apple.com/videos/play/wwdc2018/230/) is highly recommended.

This is an example extra view class that wil be an unique element for VoiceOver and will read Title and Text.

```swift
class ExtraView: UIView {
private let titleLabel = UILabel()
private let textLabel = UILabel()

// Initializers...

func configure(title: String, text: String) {
titleLabel.text = title
textLabel.text = text

// Update the accessibility label with the
// content to be readed by VoiceOver
accessibilityLabel = "\(title) \(text)"
}

private func commonInit() {
// Setup code...

// Make this view as an accesible element to
// be focusable by VoiceOver.
isAccessibleElement = true
}
}

card.fragmentView = extraView
```


## Cards as Cells (UITableViewCell/UICollectionViewCell)

To use the cards as cells, as selectable items, set the `accessibilityTraits` of the Card to `[.button]` is recommended.
And double tap with the focused card will launch `didSelect` on table/collection delegate.

## UITapGesture

Same as working with cells, Cards can be tappables with `UITapGestureRecognizer`, setting the `accessibilityTrait` to `[.button]` is also recommended.

0 comments on commit 3bbe36b

Please sign in to comment.