Skip to content

Commit

Permalink
feat(Accessibility): IOS-10448 [Mistica] RowList A11y update (#399)
Browse files Browse the repository at this point in the history
* As pat of IOS-10347 we need to set sheet's title label accessibility trait to header

* Added new protocol for accessible text views
Added full cell accessbility config struct
First approach for improving cells accessibility

* Added accessibility type to lists catalog to be able to test it
Update ListCellContent accessibility (via delegate) when headlineView is updated
Fixed some typos
Deleted no longer used FullCellAccessibilityConfig and created accessibility type
Created separate files for list cell accessibility data

* Moved method to right place

* Added double interaction accessibility type
Added comments to help understanding this new feature

* Updated comment

* Swiftformat

* Added custom label for interactive cell in catalog

* Fixed PR comments
Present interactive cell alert in tabbarcontroller to avoid warning
  • Loading branch information
dhidalgofadrique authored Sep 12, 2024
1 parent fe554bb commit 023b62c
Show file tree
Hide file tree
Showing 8 changed files with 265 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ private enum Section: Int, CaseIterable {
case headline
case assetStyle
case controlType
case accessibilityType
case show
}

Expand Down Expand Up @@ -77,6 +78,17 @@ class UICatalogListsViewController: UITableViewController {
return cell
}()

private lazy var accessibilityTypeCell: UISegmentedControlTableViewCell = {
let cell = UISegmentedControlTableViewCell(reuseIdentifier: "accessibilityTypeCell")
cell.segmentedControl.insertSegment(withTitle: "Default", at: 0, animated: false)
cell.segmentedControl.insertSegment(withTitle: "Informative", at: 1, animated: false)
cell.segmentedControl.insertSegment(withTitle: "Custom informative", at: 2, animated: false)
cell.segmentedControl.insertSegment(withTitle: "Interactive", at: 3, animated: false)
cell.segmentedControl.insertSegment(withTitle: "Double interaction", at: 4, animated: false)
cell.segmentedControl.selectedSegmentIndex = 0
return cell
}()

private lazy var showListCell: UITableViewCell = {
let cell = UITableViewCell(style: .default, reuseIdentifier: "showListCell")
cell.textLabel?.textColor = .textLink
Expand All @@ -92,6 +104,7 @@ class UICatalogListsViewController: UITableViewController {
[headlineCell],
[assetStyleCell],
[controlCell],
[accessibilityTypeCell],
[showListCell]
]

Expand Down Expand Up @@ -186,6 +199,28 @@ extension UICatalogListsViewController {
break
}

switch accessibilityTypeCell.segmentedControl.selectedSegmentIndex {
case 0:
sampleVC.accessibilityType = .default
case 1:
sampleVC.accessibilityType = .informative
case 2:
sampleVC.accessibilityType = .customInformative("Custom informative label")
case 3:
let accessibilityInteractiveData = AccessibilityListCellInteractiveData(label: "Custom action cell. Tap to execute custom action") { [weak self] in
let alertController = UIAlertController(title: nil, message: "Custom action", preferredStyle: .alert)
let alertAction = UIAlertAction(title: "Accept", style: .cancel)
alertController.addAction(alertAction)

self?.tabBarController?.present(alertController, animated: true)
}
sampleVC.accessibilityType = .interactive(accessibilityInteractiveData)
case 4:
sampleVC.accessibilityType = .doubleInteraction(.default)
default:
break
}

show(sampleVC, sender: self)
tableView.deselectRow(animated: true)
view.endEditing(true)
Expand All @@ -209,6 +244,8 @@ private extension Section {
return "Asset Style"
case .controlType:
return "Control Type"
case .accessibilityType:
return "Accessibility Type"
case .show:
return nil
}
Expand All @@ -231,6 +268,7 @@ private class UICatalogListSampleViewController: UIViewController, UITableViewDa
var assetType: ListCellContentView.CellAssetType!
var customControl = CustomControl.none
var cellLayoutStyle: ListCellContentView.CellStyle!
var accessibilityType: AccessibilityListCellType!

let numberOfRows = 30

Expand Down Expand Up @@ -283,6 +321,7 @@ private class UICatalogListSampleViewController: UIViewController, UITableViewDa
}

cell.isCellSeparatorHidden = indexPath.row == (numberOfRows - 1)
cell.accessibilityType = accessibilityType

return cell
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,32 @@

import UIKit

protocol ListCellContentViewDelegate: AnyObject {
func accessibilityChanged()
}

class CellCenterSectionView: UIStackView {
var headlineView: UIView? {
var accessibilityActivationAction: (() -> Void)?

var headlineView: AccessibleTextualView? {
didSet {
oldValue?.removeFromSuperview()

if let view = headlineView {
insertArrangedSubview(view, at: 0)
updateSpacing()
}

listCellContentViewDelegate?.accessibilityChanged()
}
}

lazy var titleLabel = IntrinsictHeightLabel()
lazy var subtitleLabel = IntrinsictHeightLabel()
lazy var detailLabel = IntrinsictHeightLabel()

weak var listCellContentViewDelegate: ListCellContentViewDelegate?

var titleTextColor: UIColor = .textPrimary {
didSet {
titleLabel.textColor = titleTextColor
Expand Down Expand Up @@ -70,6 +80,15 @@ class CellCenterSectionView: UIStackView {
}
}

override public func accessibilityActivate() -> Bool {
guard let accessibilityActivationAction else {
return false
}

accessibilityActivationAction()
return true
}

func didSetTextToSubtitleLabel() {
if !hasSubtitleText {
subtitleLabel.removeFromSuperview()
Expand Down
85 changes: 80 additions & 5 deletions Sources/Mistica/Components/Lists/ListCellContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,51 @@ import UIKit

protocol ListCellContentTableViewDelegate {
func cellStyleChanged()
func accessibilityChanged()
}

// MARK: ListCellContentView

open class ListCellContentView: UIView {
// MARK: Accessibility properties

var accessibilityType: AccessibilityListCellType = .default {
didSet {
if case .doubleInteraction(let accessibilityInteractiveData) = accessibilityType {
// If double interaction accessibility, make centerSection accessible to be focusable (isAccessibilityElement = true)
centerSection.isAccessibilityElement = true
// Set center section label to the provided one (or the default one if not provided)
centerSection.accessibilityLabel = accessibilityInteractiveData.label ?? defaultAccessibilityLabel
// Set accessibility activation action to be executed on center section double tap
centerSection.accessibilityActivationAction = accessibilityInteractiveData.action
} else {
// If any other accessibility type, it's managed in the superview (ListTableViewCell)
centerSection.isAccessibilityElement = false
centerSection.accessibilityLabel = nil
centerSection.accessibilityActivationAction = nil
}
updateAccessibilityElements()
}
}

// Default accessibilityLabel using the order specified in the Figma spec:
// https://www.figma.com/design/Be8QB9onmHunKCCAkIBAVr/%F0%9F%94%B8-Lists-Specs?node-id=0-1&node-type=CANVAS&t=jgG9X5qKokaMwJjm-0
var defaultAccessibilityLabel: String {
let titleText = titleAccessibilityLabel ?? titleAttributedText?.string ?? title
let subtitleText = subtitleAccessibilityLabel ?? subtitleAttributedText?.string ?? subtitle
let detailText = detailAccessibilityLabel ?? detailTextAttributedText?.string ?? detailText
let headlineText = headlineView?.accessibleText

let accessibilityComponents: [String?] = [
titleText,
headlineText,
subtitleText,
detailText
]

return accessibilityComponents.compactMap { $0 }.joined(separator: ", ")
}

// MARK: View Styles

public enum ViewStyles {
Expand Down Expand Up @@ -58,7 +98,7 @@ open class ListCellContentView: UIView {

// MARK: SubViews

/// View used in `ListCellStyle.boxed` style for show a rounded border arround the content
/// View used in `ListCellStyle.boxed` style for show a rounded border around the content
lazy var cellBorderView = UIView()
private lazy var cellContentView = UIStackView()
var tableViewDelegate: ListCellContentTableViewDelegate?
Expand All @@ -81,7 +121,8 @@ open class ListCellContentView: UIView {
}
set {
centerSection.titleLabel.text = newValue
updateAssetAligment()
updateAssetAlignment()
updateAccessibility()
}
}

Expand All @@ -100,6 +141,7 @@ open class ListCellContentView: UIView {
}
set {
centerSection.titleLabel.attributedText = newValue
updateAccessibility()
}
}

Expand All @@ -117,6 +159,7 @@ open class ListCellContentView: UIView {
centerSection.subtitleLabel.text = newValue
centerSection.didSetTextToSubtitleLabel()
updateAssetView()
updateAccessibility()
}
}

Expand All @@ -136,6 +179,7 @@ open class ListCellContentView: UIView {
set {
centerSection.subtitleLabel.attributedText = newValue
centerSection.didSetTextToSubtitleLabel()
updateAccessibility()
}
}

Expand All @@ -147,6 +191,7 @@ open class ListCellContentView: UIView {
centerSection.detailLabel.text = newValue
centerSection.didSetTexToDetailText()
updateAssetView()
updateAccessibility()
}
}

Expand All @@ -166,16 +211,18 @@ open class ListCellContentView: UIView {
set {
centerSection.detailLabel.attributedText = newValue
centerSection.didSetTexToDetailText()
updateAccessibility()
}
}

public var headlineView: UIView? {
public var headlineView: AccessibleTextualView? {
get {
centerSection.headlineView
}
set {
centerSection.headlineView = newValue
updateAssetView()
updateAccessibility()
}
}

Expand Down Expand Up @@ -327,12 +374,22 @@ public extension ListCellContentView {
}
}

// MARK: ListCellContentViewDelegate

extension ListCellContentView: ListCellContentViewDelegate {
func accessibilityChanged() {
updateAccessibilityElements()
}
}

// MARK: Private

private extension ListCellContentView {
func commonInit() {
centerSection.listCellContentViewDelegate = self
layoutViews()
updateCellStyle()
updateAccessibilityElements()
}

func layoutViews() {
Expand Down Expand Up @@ -369,7 +426,7 @@ private extension ListCellContentView {
return
}

updateAssetAligment()
updateAssetAlignment()

leftSection.assetType = assetType

Expand All @@ -378,11 +435,29 @@ private extension ListCellContentView {
}
}

func updateAssetAligment() {
func updateAssetAlignment() {
if centerSection.headlineView == nil, !centerSection.hasSubtitleText, !centerSection.hasDetailText {
leftSection.centerAlignment()
} else {
leftSection.topAlignment()
}
}

func updateAccessibility() {
tableViewDelegate?.accessibilityChanged()
}

func updateAccessibilityElements() {
switch accessibilityType {
case .informative:
// Set accessibility order following Figma spec:
// https://www.figma.com/design/Be8QB9onmHunKCCAkIBAVr/%F0%9F%94%B8-Lists-Specs?node-id=0-1&node-type=CANVAS&t=jgG9X5qKokaMwJjm-0
accessibilityElements = [centerSection.titleLabel, headlineView as Any, centerSection.subtitleLabel, centerSection.detailLabel, controlView as Any].compactMap { $0 }
case .doubleInteraction:
// If double interaction, just two elements: center section and right section
accessibilityElements = [centerSection, controlView as Any].compactMap { $0 }
case .interactive, .customInformative:
accessibilityElements = []
}
}
}
Loading

0 comments on commit 023b62c

Please sign in to comment.