Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IOS-10448 [Mistica] RowList A11y update #399

Merged
merged 10 commits into from
Sep 12, 2024
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
}()
Comment on lines +81 to +90
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New cell to configure accessibility

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only see a small issue and that is that the literals can not be read as a whole.
Screenshot 2024-09-10 at 15 35 11

Perhaps it would be interesting to rotate the segmented control vertically XD
https://stackoverflow.com/questions/3490358/can-i-show-an-uisegmentedcontrol-object-in-vertical

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried rotation with no success xD. It seems also not possible to modify segmented control to allow multiple lines. Maybe the solution is to create a new cell type for "many" options using buttons


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?.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 {
func accessibilityChanged()
}

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

var headlineView: AccessibleTextualView? {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to it's possible to use any UIView, used a protocol that exposes an accessible text

didSet {
oldValue?.removeFromSuperview()

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

listCellContentViewDelegate?.accessibilityChanged()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notify delegate that accessibility has changed

}
}

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

var listCellContentViewDelegate: ListCellContentViewDelegate?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weak?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1


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
}
Comment on lines +83 to +90
Copy link
Contributor Author

@dhidalgofadrique dhidalgofadrique Sep 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Execute custom accessibility action on double tap if it's defined. This case only occurs in cells with accessibility of type doubleInteraction when double tap the center section (for cells with interactive accesibility type, this action is managed in the parent cell view)


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
Comment on lines +23 to +29
Copy link
Contributor Author

@dhidalgofadrique dhidalgofadrique Sep 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case of double interaction accessibility, center section has to be accessible to be focusable by VoiceOver. The label associated will be the provided one or if no label provided, the default one (defined in Figma specs) and the optional accessibility action is stored

} 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
]
Comment on lines +48 to +53
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Composed default accessibility label following Figma specs


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 }
Comment on lines +453 to +455
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If accessibility is informative, define elements order according Figma specs

case .doubleInteraction:
// If double interaction, just two elements: center section and right section
accessibilityElements = [centerSection, controlView as Any].compactMap { $0 }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case of double interaction accessibility, there will be two elements: center section (which will manage it's own accessibility) and the control view

case .interactive, .customInformative:
accessibilityElements = []
}
}
}
Loading
Loading