From d55ae3c2ff7a54ca1362bc49e8fc72d8dd0b62e7 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Mon, 22 May 2023 19:07:16 +0300 Subject: [PATCH 001/126] make init --- Brewfile.lock.json | 60 +++++++++++++++++++++++----------------------- Gemfile.lock | 10 ++++---- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Brewfile.lock.json b/Brewfile.lock.json index c793d29bf..d06cb711a 100644 --- a/Brewfile.lock.json +++ b/Brewfile.lock.json @@ -2,16 +2,16 @@ "entries": { "tap": { "homebrew/cask": { - "revision": "3ece2c193e9019ec49037b8f829ab42d88ece146" + "revision": "266747f8b172dd47e6c80ce2590889a8a7c96501" }, "yonaskolb/xcodegen": { - "revision": "3327c44ad74c1e04e0de1e8ecca0d7114a613765", + "revision": "372f20fe5a3823dc398f52cb6cc1af5003d452f7", "options": { "clone_target": "https://github.com/yonaskolb/XcodeGen.git" } }, "krzysztofzablocki/sourcery": { - "revision": "3a9e5f1014ac09b30727c01d2a0e1531dac30948", + "revision": "cc4252f1d62f7c44e5c757417a45ee0f56c889d2", "options": { "clone_target": "https://github.com/krzysztofzablocki/Sourcery.git" } @@ -19,69 +19,69 @@ }, "brew": { "xcodegen": { - "version": "2.33.0", + "version": "2.25.0", "bottle": { "rebuild": 0, "root_url": "https://ghcr.io/v2/homebrew/core", "files": { "arm64_ventura": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:76d47e558129e02cb16dbcdbb92b25ac5ab47658f4736cd1f68d205bebcd0001", - "sha256": "76d47e558129e02cb16dbcdbb92b25ac5ab47658f4736cd1f68d205bebcd0001" + "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:ca9aa5cf7c7a4573bd3ff2c1afbe871566e57478c09967abca7585fc1c237470", + "sha256": "ca9aa5cf7c7a4573bd3ff2c1afbe871566e57478c09967abca7585fc1c237470" }, "arm64_monterey": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:092164ce460d49adbc33023f55ec6b9a5704d787e18c05ac7d62232c47bdc91e", - "sha256": "092164ce460d49adbc33023f55ec6b9a5704d787e18c05ac7d62232c47bdc91e" + "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:dadb3716295017a392da394eb0bb52360d95e9142e21ac5532e1d29def593ad6", + "sha256": "dadb3716295017a392da394eb0bb52360d95e9142e21ac5532e1d29def593ad6" }, "arm64_big_sur": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:b4ba5b91c37a2b3b364a5af35d207eac215a2c00987d008a3002a9d643d39652", - "sha256": "b4ba5b91c37a2b3b364a5af35d207eac215a2c00987d008a3002a9d643d39652" + "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:6026a0e84873586f7e65584bd3e9a9456f4b590c2498ebf986e563d66bccb3a7", + "sha256": "6026a0e84873586f7e65584bd3e9a9456f4b590c2498ebf986e563d66bccb3a7" }, "ventura": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:38a9e261286f6f7e402eef108a6b0fc02884a9872d9464d69a56b1adc725c7fe", - "sha256": "38a9e261286f6f7e402eef108a6b0fc02884a9872d9464d69a56b1adc725c7fe" + "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:bb982470aad36dececfab94449696b1a327ea931b61475832fd41c5fa59ebb60", + "sha256": "bb982470aad36dececfab94449696b1a327ea931b61475832fd41c5fa59ebb60" }, "monterey": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:a05217b82deaa2efa0ee832b663232aab66aabe2d2c08ad2e3fde723b6faffb0", - "sha256": "a05217b82deaa2efa0ee832b663232aab66aabe2d2c08ad2e3fde723b6faffb0" + "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:366403595292790a4a84e2db6f0f32c14fa907c77ac424a5e24d431d372e7a17", + "sha256": "366403595292790a4a84e2db6f0f32c14fa907c77ac424a5e24d431d372e7a17" }, "big_sur": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:f5b9313d25bff493ea073b291c2a86d3633cc16ec331fa3d2caf8a1131247bbb", - "sha256": "f5b9313d25bff493ea073b291c2a86d3633cc16ec331fa3d2caf8a1131247bbb" + "url": "https://ghcr.io/v2/homebrew/core/xcodegen/blobs/sha256:6db46a3673d4cd395cabd22bbe28e701eef7eaa4f198829fd8a4df072eb9a6ff", + "sha256": "6db46a3673d4cd395cabd22bbe28e701eef7eaa4f198829fd8a4df072eb9a6ff" } } } }, "sourcery": { - "version": "1.9.2", + "version": "1.8.1", "bottle": { "rebuild": 0, "root_url": "https://ghcr.io/v2/homebrew/core", "files": { "arm64_ventura": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/sourcery/blobs/sha256:37f85dd2fc0091ba35abf1bc059ac2ab9eb7490955b327445f52d5c92a26fd44", - "sha256": "37f85dd2fc0091ba35abf1bc059ac2ab9eb7490955b327445f52d5c92a26fd44" + "url": "https://ghcr.io/v2/homebrew/core/sourcery/blobs/sha256:bafccb509c0586fd8725582aec7c7da7fe88055c7a8608d4ca0f5803a37988fb", + "sha256": "bafccb509c0586fd8725582aec7c7da7fe88055c7a8608d4ca0f5803a37988fb" }, "arm64_monterey": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/sourcery/blobs/sha256:5bd5a098011084a2eb0a967382a93997975535195badd3ad5807e3cd19fdcf6e", - "sha256": "5bd5a098011084a2eb0a967382a93997975535195badd3ad5807e3cd19fdcf6e" + "url": "https://ghcr.io/v2/homebrew/core/sourcery/blobs/sha256:f3d3c5407c9de6d7eb9d6dfdffa4fb01350c31d6d335bc26ff94eea7ed7e66c9", + "sha256": "f3d3c5407c9de6d7eb9d6dfdffa4fb01350c31d6d335bc26ff94eea7ed7e66c9" }, "ventura": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/sourcery/blobs/sha256:3be6812ef327e5e0d9ba0b52c6809e89ec74a4effdcefc4968f7fbc369efcbe2", - "sha256": "3be6812ef327e5e0d9ba0b52c6809e89ec74a4effdcefc4968f7fbc369efcbe2" + "url": "https://ghcr.io/v2/homebrew/core/sourcery/blobs/sha256:9d6ed066d64296432ac9a1bc91aef8606c5d966c56f535fff011ae39fbea9746", + "sha256": "9d6ed066d64296432ac9a1bc91aef8606c5d966c56f535fff011ae39fbea9746" }, "monterey": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/sourcery/blobs/sha256:26816c3345810c1b3e154dbdb6b273ea59ba87394dc9773092beee1d223ff0b6", - "sha256": "26816c3345810c1b3e154dbdb6b273ea59ba87394dc9773092beee1d223ff0b6" + "url": "https://ghcr.io/v2/homebrew/core/sourcery/blobs/sha256:039e1516d4cbdcb7107ac7a1777f9cb11891ef36a4b5f12ae6dd57b6157b6b3e", + "sha256": "039e1516d4cbdcb7107ac7a1777f9cb11891ef36a4b5f12ae6dd57b6157b6b3e" } } } @@ -107,12 +107,12 @@ "macOS": "11.5.1" }, "ventura": { - "HOMEBREW_VERSION": "3.6.15-53-g4534748", - "HOMEBREW_PREFIX": "/usr/local", - "Homebrew/homebrew-core": "d0befea5025acac52ece7e6c67966708f025c2af", + "HOMEBREW_VERSION": "4.0.18", + "HOMEBREW_PREFIX": "/opt/homebrew", + "Homebrew/homebrew-core": "api", "CLT": "", - "Xcode": "14.2", - "macOS": "13.0.1" + "Xcode": "14.3", + "macOS": "13.4" } } } diff --git a/Gemfile.lock b/Gemfile.lock index 9e918d70d..45c2d2bcf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -64,7 +64,7 @@ GEM fuzzy_match (2.0.4) gh_inspector (1.1.3) httpclient (2.8.3) - i18n (1.12.0) + i18n (1.13.0) concurrent-ruby (~> 1.0) json (2.6.3) minitest (5.18.0) @@ -72,9 +72,9 @@ GEM nanaimo (0.3.0) nap (1.1.0) netrc (0.11.0) - nokogiri (1.14.3-arm64-darwin) + nokogiri (1.15.1-arm64-darwin) racc (~> 1.4) - nokogiri (1.14.3-x86_64-darwin) + nokogiri (1.15.1-x86_64-darwin) racc (~> 1.4) public_suffix (4.0.7) racc (1.6.2) @@ -100,7 +100,7 @@ GEM rexml (~> 3.2.4) xcpretty (0.3.0) rouge (~> 2.0.7) - zeitwerk (2.6.7) + zeitwerk (2.6.8) PLATFORMS arm64-darwin-22 @@ -112,4 +112,4 @@ DEPENDENCIES xcpretty (~> 0.3.0) BUNDLED WITH - 2.2.33 + 2.4.6 From a6a97cf6e2a9a12882a254ed44390392d04c024a Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Mon, 22 May 2023 19:08:06 +0300 Subject: [PATCH 002/126] added accessibility parser --- Source/Utils/AccessibilityParser.swift | 58 ++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Source/Utils/AccessibilityParser.swift diff --git a/Source/Utils/AccessibilityParser.swift b/Source/Utils/AccessibilityParser.swift new file mode 100644 index 000000000..8d0b12f00 --- /dev/null +++ b/Source/Utils/AccessibilityParser.swift @@ -0,0 +1,58 @@ +// +// AccessibilityParser.swift +// ReactiveDataDisplayManager +// +// Created by korshunov on 22.05.2023. +// + +import UIKit + +struct AccessibilityItemInfo { + var accessibilityElements: [NSObject] + + var containsAction: Bool { + accessibilityElements.contains(where: { $0 is UIButton }) + } + + var isEmpty: Bool { + accessibilityElements.isEmpty + } +} + +enum AccessibilityParser { + static func process(view: UIView) -> AccessibilityItemInfo { + if view.isAccessibilityElement { + return AccessibilityItemInfo(accessibilityElements: [view]) + } else if view.subviews.isEmpty { + return AccessibilityItemInfo(accessibilityElements: []) + } + + var accessibilityElements = view.subviews.filter(\.isAccessibilityElement) as [NSObject] + if accessibilityElements.isEmpty { + accessibilityElements = view.subviews.flatMap { process(view: $0).accessibilityElements } + } + return AccessibilityItemInfo(accessibilityElements: accessibilityElements) + } + + static func apply(info: AccessibilityItemInfo, to view: UIView) { + guard !info.isEmpty else { + view.isAccessibilityElement = false + return + } + view.isAccessibilityElement = true + + if let element = info.accessibilityElements.first { + view.accessibilityLabel = element.accessibilityLabel + view.accessibilityValue = element.accessibilityValue + view.accessibilityTraits = element.accessibilityTraits + } + if let subElement = info.accessibilityElements[safe: 1] { + view.accessibilityValue = subElement.accessibilityLabel + } + } + + static func processAndApply(process view: UIView, applyTo viewToApply: UIView) { + let info = process(view: view) + apply(info: info, to: viewToApply) + } +} From 3531e7176dae96ff35f75ac5c801013c25ad8b7c Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Mon, 22 May 2023 19:08:29 +0300 Subject: [PATCH 003/126] added accessibility plugin --- Source/Collection/CollectionBuilder.swift | 1 + .../Delegate/BaseCollectionDelegate.swift | 8 +-- .../Collection/Events/CollectionEvent.swift | 8 +-- .../CollectionAccessibilityPlugin.swift | 53 +++++++++++++++++++ .../CollectionPaginatablePlugin.swift | 2 +- .../Generators/CustomAccessibilityItem.swift | 13 +++++ 6 files changed, 76 insertions(+), 9 deletions(-) create mode 100644 Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift create mode 100644 Source/Protocols/Plugins/Generators/CustomAccessibilityItem.swift diff --git a/Source/Collection/CollectionBuilder.swift b/Source/Collection/CollectionBuilder.swift index 6c4a6b9fa..003615705 100644 --- a/Source/Collection/CollectionBuilder.swift +++ b/Source/Collection/CollectionBuilder.swift @@ -136,6 +136,7 @@ public class CollectionBuilder { /// Build delegate, dataSource, view and data display manager together and returns DataDisplayManager public func build() -> T { + collectionPlugins.add(.accessibility()) manager.view = view animator = QueuedAnimator(baseAnimator: CollectionSafeAnimator(baseAnimator: animator, diff --git a/Source/Collection/Delegate/BaseCollectionDelegate.swift b/Source/Collection/Delegate/BaseCollectionDelegate.swift index e0d720627..6b314cf48 100644 --- a/Source/Collection/Delegate/BaseCollectionDelegate.swift +++ b/Source/Collection/Delegate/BaseCollectionDelegate.swift @@ -91,11 +91,11 @@ extension BaseCollectionDelegate { } open func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - collectionPlugins.process(event: .willDisplayCell(indexPath), with: manager) + collectionPlugins.process(event: .willDisplayCell(indexPath, cell), with: manager) } open func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - collectionPlugins.process(event: .didEndDisplayCell(indexPath), with: manager) + collectionPlugins.process(event: .didEndDisplayCell(indexPath, cell), with: manager) } open func collectionView(_ collectionView: UICollectionView, canFocusItemAt indexPath: IndexPath) -> Bool { @@ -120,13 +120,13 @@ extension BaseCollectionDelegate { open func collectionView(_ collectionView: UICollectionView, willDisplaySupplementaryView view: UICollectionReusableView, forElementKind elementKind: String, at indexPath: IndexPath) { - collectionPlugins.process(event: .willDisplaySupplementaryView(indexPath), with: manager) + collectionPlugins.process(event: .willDisplaySupplementaryView(indexPath, view, elementKind), with: manager) } open func collectionView(_ collectionView: UICollectionView, didEndDisplayingSupplementaryView view: UICollectionReusableView, forElementOfKind elementKind: String, at indexPath: IndexPath) { - collectionPlugins.process(event: .didEndDisplayingSupplementaryView(indexPath), with: manager) + collectionPlugins.process(event: .didEndDisplayingSupplementaryView(indexPath, view, elementKind), with: manager) } } diff --git a/Source/Collection/Events/CollectionEvent.swift b/Source/Collection/Events/CollectionEvent.swift index 3ba383ddd..19a0221dd 100644 --- a/Source/Collection/Events/CollectionEvent.swift +++ b/Source/Collection/Events/CollectionEvent.swift @@ -13,9 +13,9 @@ public enum CollectionEvent { case didDeselect(IndexPath) case didHighlight(IndexPath) case didUnhighlight(IndexPath) - case willDisplayCell(IndexPath) - case didEndDisplayCell(IndexPath) - case willDisplaySupplementaryView(IndexPath) - case didEndDisplayingSupplementaryView(IndexPath) + case willDisplayCell(IndexPath, UICollectionViewCell) + case didEndDisplayCell(IndexPath, UICollectionViewCell) + case willDisplaySupplementaryView(IndexPath, UICollectionReusableView, String) + case didEndDisplayingSupplementaryView(IndexPath, UICollectionReusableView, String) case move(from: IndexPath, to: IndexPath) } diff --git a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift new file mode 100644 index 000000000..56da9a3ff --- /dev/null +++ b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift @@ -0,0 +1,53 @@ +// +// CollectionAccessibilityPlugin.swift +// ReactiveDataDisplayManager +// +// Created by korshunov on 22.05.2023. +// + +import UIKit + +final class CollectionAccessibilityPlugin: BaseCollectionPlugin { + + override func process(event: CollectionEvent, with manager: BaseCollectionManager?) { + switch event { + case .willDisplayCell(let indexPath, let cell): + if let customItem = cell as? CustomAccessibilityItem { + customItem.setupAccessibility() + } else { + AccessibilityParser.processAndApply(process: cell.contentView, applyTo: cell) + if let generator = manager?.generators[indexPath.section][indexPath.row], + generator is SelectableItem || generator is FoldableItem { + cell.accessibilityTraits.insert(.button) + } + } + case .willDisplaySupplementaryView(let indexPath, let view, let elementKind): + if let customItem = view as? CustomAccessibilityItem { + customItem.setupAccessibility() + } else { + AccessibilityParser.processAndApply(process: view, applyTo: view) + var generator: Any? + switch elementKind { + case UICollectionView.elementKindSectionHeader: + generator = manager?.sections[indexPath.section] + view.accessibilityTraits.insert(.header) + case UICollectionView.elementKindSectionFooter: + generator = manager?.footers[indexPath.section] + default: + break + } + if let generator = generator, generator is SelectableItem || generator is FoldableItem { + view.accessibilityTraits.insert(.button) + } + } + default: + break + } + } +} + +extension BaseCollectionPlugin { + static func accessibility() -> BaseCollectionPlugin { + CollectionAccessibilityPlugin() + } +} diff --git a/Source/Collection/Plugins/PluginAction/CollectionPaginatablePlugin.swift b/Source/Collection/Plugins/PluginAction/CollectionPaginatablePlugin.swift index 386f009c9..bbf58faab 100644 --- a/Source/Collection/Plugins/PluginAction/CollectionPaginatablePlugin.swift +++ b/Source/Collection/Plugins/PluginAction/CollectionPaginatablePlugin.swift @@ -76,7 +76,7 @@ public class CollectionPaginatablePlugin: BaseCollectionPlugin public override func process(event: CollectionEvent, with manager: BaseCollectionManager?) { switch event { - case .willDisplayCell(let indexPath): + case .willDisplayCell(let indexPath, _): guard let generators = manager?.generators else { return } diff --git a/Source/Protocols/Plugins/Generators/CustomAccessibilityItem.swift b/Source/Protocols/Plugins/Generators/CustomAccessibilityItem.swift new file mode 100644 index 000000000..c98e068ef --- /dev/null +++ b/Source/Protocols/Plugins/Generators/CustomAccessibilityItem.swift @@ -0,0 +1,13 @@ +// +// CustomAccessibilityItem.swift +// ReactiveDataDisplayManager +// +// Created by korshunov on 22.05.2023. +// + +import UIKit + +public protocol CustomAccessibilityItem { + func setupAccessibility() +} + From 8414389a88146509ce5df6a38a3619eb2bd8f943 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Wed, 24 May 2023 11:46:13 +0300 Subject: [PATCH 004/126] added accessibility strategies --- .../CollectionAccessibilityPlugin.swift | 33 ++----- .../Generators/AccessibilityItem.swift | 90 +++++++++++++++++++ .../Generators/CustomAccessibilityItem.swift | 13 --- Source/Utils/AccessibilityModifier.swift | 33 +++++++ Source/Utils/AccessibilityParser.swift | 58 ------------ 5 files changed, 130 insertions(+), 97 deletions(-) create mode 100644 Source/Protocols/Plugins/Generators/AccessibilityItem.swift delete mode 100644 Source/Protocols/Plugins/Generators/CustomAccessibilityItem.swift create mode 100644 Source/Utils/AccessibilityModifier.swift delete mode 100644 Source/Utils/AccessibilityParser.swift diff --git a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift index 56da9a3ff..dd61511fb 100644 --- a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift +++ b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift @@ -12,33 +12,14 @@ final class CollectionAccessibilityPlugin: BaseCollectionPlugin override func process(event: CollectionEvent, with manager: BaseCollectionManager?) { switch event { case .willDisplayCell(let indexPath, let cell): - if let customItem = cell as? CustomAccessibilityItem { - customItem.setupAccessibility() - } else { - AccessibilityParser.processAndApply(process: cell.contentView, applyTo: cell) - if let generator = manager?.generators[indexPath.section][indexPath.row], - generator is SelectableItem || generator is FoldableItem { - cell.accessibilityTraits.insert(.button) - } + let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityItem + if let accessibilityItem = generator ?? cell as? AccessibilityItem { + accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem) } - case .willDisplaySupplementaryView(let indexPath, let view, let elementKind): - if let customItem = view as? CustomAccessibilityItem { - customItem.setupAccessibility() - } else { - AccessibilityParser.processAndApply(process: view, applyTo: view) - var generator: Any? - switch elementKind { - case UICollectionView.elementKindSectionHeader: - generator = manager?.sections[indexPath.section] - view.accessibilityTraits.insert(.header) - case UICollectionView.elementKindSectionFooter: - generator = manager?.footers[indexPath.section] - default: - break - } - if let generator = generator, generator is SelectableItem || generator is FoldableItem { - view.accessibilityTraits.insert(.button) - } + case .willDisplaySupplementaryView(let indexPath, let view, _): + let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityItem + if let accessibilityItem = generator ?? view as? AccessibilityItem { + accessibilityItem.modifierType.modify(view: view, with: accessibilityItem) } default: break diff --git a/Source/Protocols/Plugins/Generators/AccessibilityItem.swift b/Source/Protocols/Plugins/Generators/AccessibilityItem.swift new file mode 100644 index 000000000..c9ff9fed2 --- /dev/null +++ b/Source/Protocols/Plugins/Generators/AccessibilityItem.swift @@ -0,0 +1,90 @@ +// +// AccessibilityItem.swift +// ReactiveDataDisplayManager +// +// Created by korshunov on 22.05.2023. +// + +import UIKit + +public enum AccessibilityStrategy { + case ignored + case just(String?) + case from(NSObject, keyPath: KeyPath = \.accessibilityLabel) + indirect case joined([AccessibilityStrategy], separator: String) + + var isIgnored: Bool { + if case .ignored = self { + return true + } else { + return false + } + } + + var value: String? { + switch self { + case .ignored: + return nil + case .just(let string): + return string + case .from(let object, let keyPath): + return object[keyPath: keyPath] + case .joined(let cases, let separator): + return cases.compactMap(\.value).joined(separator: separator) + } + } +} + +public enum AccessibilityTraitsStrategy { + case ignored + case just(UIAccessibilityTraits) + case from(NSObject) + case merge([NSObject]) + + var isIgnored: Bool { + if case .ignored = self { + return true + } else { + return false + } + } + + var value: UIAccessibilityTraits? { + switch self { + case .ignored: + return nil + case .just(let traits): + return traits + case .from(let object): + return object.accessibilityTraits + case .merge(let objects): + return objects.map(\.accessibilityTraits).reduce(UIAccessibilityTraits(), { $0.union($1) }) + } + } + +// mutating func insert(traits: UIAccessibilityTraits) { +// self = .just(value.map { $0.union(traits) } ?? traits) +// } +// +// mutating func remove(traits: UIAccessibilityTraits) { +// self = .just(value.map { $0.subtracting(traits) } ?? traits) +// } +} + +public protocol AccessibilityItem { + typealias AccessibilityModifierType = AccessibilityModifier.Type + var modifierType: AccessibilityModifierType { get } + + var labelStrategy: AccessibilityStrategy { get } + var valueStrategy: AccessibilityStrategy { get } + var traitsStrategy: AccessibilityTraitsStrategy { get } +} + +public extension AccessibilityItem { + var parcerType: AccessibilityModifierType { DefaultAccessibilityModifier.self } + + var labelStrategy: AccessibilityStrategy { .ignored } + var valueStrategy: AccessibilityStrategy { .ignored } + var traitsStrategy: AccessibilityTraitsStrategy { .ignored } +} + diff --git a/Source/Protocols/Plugins/Generators/CustomAccessibilityItem.swift b/Source/Protocols/Plugins/Generators/CustomAccessibilityItem.swift deleted file mode 100644 index c98e068ef..000000000 --- a/Source/Protocols/Plugins/Generators/CustomAccessibilityItem.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// CustomAccessibilityItem.swift -// ReactiveDataDisplayManager -// -// Created by korshunov on 22.05.2023. -// - -import UIKit - -public protocol CustomAccessibilityItem { - func setupAccessibility() -} - diff --git a/Source/Utils/AccessibilityModifier.swift b/Source/Utils/AccessibilityModifier.swift new file mode 100644 index 000000000..00ea57b9d --- /dev/null +++ b/Source/Utils/AccessibilityModifier.swift @@ -0,0 +1,33 @@ +// +// AccessibilityModifier.swift +// ReactiveDataDisplayManager +// +// Created by korshunov on 22.05.2023. +// + +import UIKit + + +public protocol AccessibilityModifier { + static func modify(view: UIView, with item: AccessibilityItem) +} + +enum DefaultAccessibilityModifier: AccessibilityModifier { + + static func modify(view: UIView, with item: AccessibilityItem) { + view.isAccessibilityElement = true + + if !item.labelStrategy.isIgnored { + view.accessibilityLabel = item.labelStrategy.value + } + + if !item.valueStrategy.isIgnored { + view.accessibilityValue = item.valueStrategy.value + } + + if !item.traitsStrategy.isIgnored, let traits = item.traitsStrategy.value { + view.accessibilityTraits.insert(traits) + } + } + +} diff --git a/Source/Utils/AccessibilityParser.swift b/Source/Utils/AccessibilityParser.swift deleted file mode 100644 index 8d0b12f00..000000000 --- a/Source/Utils/AccessibilityParser.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// AccessibilityParser.swift -// ReactiveDataDisplayManager -// -// Created by korshunov on 22.05.2023. -// - -import UIKit - -struct AccessibilityItemInfo { - var accessibilityElements: [NSObject] - - var containsAction: Bool { - accessibilityElements.contains(where: { $0 is UIButton }) - } - - var isEmpty: Bool { - accessibilityElements.isEmpty - } -} - -enum AccessibilityParser { - static func process(view: UIView) -> AccessibilityItemInfo { - if view.isAccessibilityElement { - return AccessibilityItemInfo(accessibilityElements: [view]) - } else if view.subviews.isEmpty { - return AccessibilityItemInfo(accessibilityElements: []) - } - - var accessibilityElements = view.subviews.filter(\.isAccessibilityElement) as [NSObject] - if accessibilityElements.isEmpty { - accessibilityElements = view.subviews.flatMap { process(view: $0).accessibilityElements } - } - return AccessibilityItemInfo(accessibilityElements: accessibilityElements) - } - - static func apply(info: AccessibilityItemInfo, to view: UIView) { - guard !info.isEmpty else { - view.isAccessibilityElement = false - return - } - view.isAccessibilityElement = true - - if let element = info.accessibilityElements.first { - view.accessibilityLabel = element.accessibilityLabel - view.accessibilityValue = element.accessibilityValue - view.accessibilityTraits = element.accessibilityTraits - } - if let subElement = info.accessibilityElements[safe: 1] { - view.accessibilityValue = subElement.accessibilityLabel - } - } - - static func processAndApply(process view: UIView, applyTo viewToApply: UIView) { - let info = process(view: view) - apply(info: info, to: viewToApply) - } -} From 2db2ba447449e1c4bcf6f76e3e7b94658b041550 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Wed, 24 May 2023 15:00:13 +0300 Subject: [PATCH 005/126] added accessibility cell --- .../BaseCollectionCellGenerator.swift | 3 +- .../CollectionAccessibilityPlugin.swift | 17 +++++----- .../Generators/AccessibilityItem.swift | 31 ++++++++++--------- Source/Utils/AccessibilityModifier.swift | 23 ++++++++------ 4 files changed, 41 insertions(+), 33 deletions(-) diff --git a/Source/Collection/Generators/BaseCollectionCellGenerator.swift b/Source/Collection/Generators/BaseCollectionCellGenerator.swift index 9982176c6..7fcf1a093 100644 --- a/Source/Collection/Generators/BaseCollectionCellGenerator.swift +++ b/Source/Collection/Generators/BaseCollectionCellGenerator.swift @@ -8,7 +8,7 @@ import UIKit -open class BaseCollectionCellGenerator: SelectableItem where Cell: UICollectionViewCell { +open class BaseCollectionCellGenerator: SelectableItem, AccessibilityItem where Cell: UICollectionViewCell { // MARK: - Public Properties @@ -16,6 +16,7 @@ open class BaseCollectionCellGenerator: SelectableItem w public var didSelectEvent = BaseEvent() public var didDeselectEvent = BaseEvent() public let model: Cell.Model + public var traitsStrategy: AccessibilityTraitsStrategy = .just(.button) // MARK: - Private Properties diff --git a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift index dd61511fb..358a39702 100644 --- a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift +++ b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift @@ -12,14 +12,17 @@ final class CollectionAccessibilityPlugin: BaseCollectionPlugin override func process(event: CollectionEvent, with manager: BaseCollectionManager?) { switch event { case .willDisplayCell(let indexPath, let cell): - let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityItem - if let accessibilityItem = generator ?? cell as? AccessibilityItem { - accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem) + if let accessibilityItem = cell as? AccessibilityCell { + let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityItem + accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem, generator: generator) } - case .willDisplaySupplementaryView(let indexPath, let view, _): - let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityItem - if let accessibilityItem = generator ?? view as? AccessibilityItem { - accessibilityItem.modifierType.modify(view: view, with: accessibilityItem) + case .willDisplaySupplementaryView(let indexPath, let view, let kind): + guard [UICollectionView.elementKindSectionHeader, UICollectionView.elementKindSectionFooter].contains(kind) else { + return + } + if let accessibilityItem = view as? AccessibilityCell { + let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityItem + accessibilityItem.modifierType.modify(view: view, with: accessibilityItem, generator: generator) } default: break diff --git a/Source/Protocols/Plugins/Generators/AccessibilityItem.swift b/Source/Protocols/Plugins/Generators/AccessibilityItem.swift index c9ff9fed2..3dc45cd30 100644 --- a/Source/Protocols/Plugins/Generators/AccessibilityItem.swift +++ b/Source/Protocols/Plugins/Generators/AccessibilityItem.swift @@ -29,8 +29,8 @@ public enum AccessibilityStrategy { return string case .from(let object, let keyPath): return object[keyPath: keyPath] - case .joined(let cases, let separator): - return cases.compactMap(\.value).joined(separator: separator) + case .joined(let strategies, let separator): + return strategies.compactMap(\.value).joined(separator: separator) } } } @@ -61,30 +61,31 @@ public enum AccessibilityTraitsStrategy { return objects.map(\.accessibilityTraits).reduce(UIAccessibilityTraits(), { $0.union($1) }) } } - -// mutating func insert(traits: UIAccessibilityTraits) { -// self = .just(value.map { $0.union(traits) } ?? traits) -// } -// -// mutating func remove(traits: UIAccessibilityTraits) { -// self = .just(value.map { $0.subtracting(traits) } ?? traits) -// } } public protocol AccessibilityItem { - typealias AccessibilityModifierType = AccessibilityModifier.Type - var modifierType: AccessibilityModifierType { get } - var labelStrategy: AccessibilityStrategy { get } var valueStrategy: AccessibilityStrategy { get } var traitsStrategy: AccessibilityTraitsStrategy { get } } public extension AccessibilityItem { - var parcerType: AccessibilityModifierType { DefaultAccessibilityModifier.self } - var labelStrategy: AccessibilityStrategy { .ignored } var valueStrategy: AccessibilityStrategy { .ignored } var traitsStrategy: AccessibilityTraitsStrategy { .ignored } } +public protocol AccessibilityCell: AccessibilityItem { + typealias AccessibilityModifierType = AccessibilityModifier.Type + var modifierType: AccessibilityModifierType { get } + + func accessibilityStrategyConflictResolver(cellStrategy: AccessibilityStrategy, generatorStrategy: AccessibilityStrategy?) -> String? +} + +public extension AccessibilityCell { + var modifierType: AccessibilityModifierType { DefaultAccessibilityModifier.self } + + func accessibilityStrategyConflictResolver(cellStrategy: AccessibilityStrategy, generatorStrategy: AccessibilityStrategy?) -> String? { + return [generatorStrategy?.value, cellStrategy.value].compactMap { $0 }.joined(separator: " ") + } +} diff --git a/Source/Utils/AccessibilityModifier.swift b/Source/Utils/AccessibilityModifier.swift index 00ea57b9d..1a3806440 100644 --- a/Source/Utils/AccessibilityModifier.swift +++ b/Source/Utils/AccessibilityModifier.swift @@ -9,25 +9,28 @@ import UIKit public protocol AccessibilityModifier { - static func modify(view: UIView, with item: AccessibilityItem) + static func modify(view: UIView, with cell: AccessibilityCell, generator: AccessibilityItem?) } enum DefaultAccessibilityModifier: AccessibilityModifier { - - static func modify(view: UIView, with item: AccessibilityItem) { + static func modify(view: UIView, with cell: AccessibilityCell, generator: AccessibilityItem?) { view.isAccessibilityElement = true - if !item.labelStrategy.isIgnored { - view.accessibilityLabel = item.labelStrategy.value + if !cell.labelStrategy.isIgnored || generator?.labelStrategy.isIgnored == false { + view.accessibilityLabel = cell.accessibilityStrategyConflictResolver(cellStrategy: cell.labelStrategy, + generatorStrategy: generator?.labelStrategy) } - if !item.valueStrategy.isIgnored { - view.accessibilityValue = item.valueStrategy.value + if !cell.valueStrategy.isIgnored || generator?.valueStrategy.isIgnored == false { + view.accessibilityValue = cell.accessibilityStrategyConflictResolver(cellStrategy: cell.valueStrategy, + generatorStrategy: generator?.valueStrategy) } - if !item.traitsStrategy.isIgnored, let traits = item.traitsStrategy.value { - view.accessibilityTraits.insert(traits) + if !cell.traitsStrategy.isIgnored || generator?.traitsStrategy.isIgnored == false { + let traits = [cell.traitsStrategy.value, generator?.traitsStrategy.value] + .compactMap { $0 } + .reduce(UIAccessibilityTraits(), { $0.union($1) }) + view.accessibilityTraits = traits } } - } From ba6d2b7b9b7cffd4c2d706fcb24bbbaa25008c54 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Thu, 25 May 2023 12:58:52 +0300 Subject: [PATCH 006/126] renamed protocols --- .../CollectionAccessibilityPlugin.swift | 8 ++++---- .../Plugins/Generators/AccessibilityItem.swift | 14 +++++++------- Source/Utils/AccessibilityModifier.swift | 16 ++++++++-------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift index 358a39702..e66e4a123 100644 --- a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift +++ b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift @@ -12,16 +12,16 @@ final class CollectionAccessibilityPlugin: BaseCollectionPlugin override func process(event: CollectionEvent, with manager: BaseCollectionManager?) { switch event { case .willDisplayCell(let indexPath, let cell): - if let accessibilityItem = cell as? AccessibilityCell { - let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityItem + if let accessibilityItem = cell as? AccessibilityItem { + let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityStrategyProvider accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem, generator: generator) } case .willDisplaySupplementaryView(let indexPath, let view, let kind): guard [UICollectionView.elementKindSectionHeader, UICollectionView.elementKindSectionFooter].contains(kind) else { return } - if let accessibilityItem = view as? AccessibilityCell { - let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityItem + if let accessibilityItem = view as? AccessibilityItem { + let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityStrategyProvider accessibilityItem.modifierType.modify(view: view, with: accessibilityItem, generator: generator) } default: diff --git a/Source/Protocols/Plugins/Generators/AccessibilityItem.swift b/Source/Protocols/Plugins/Generators/AccessibilityItem.swift index 3dc45cd30..91f4cd90e 100644 --- a/Source/Protocols/Plugins/Generators/AccessibilityItem.swift +++ b/Source/Protocols/Plugins/Generators/AccessibilityItem.swift @@ -63,29 +63,29 @@ public enum AccessibilityTraitsStrategy { } } -public protocol AccessibilityItem { +public protocol AccessibilityStrategyProvider { var labelStrategy: AccessibilityStrategy { get } var valueStrategy: AccessibilityStrategy { get } var traitsStrategy: AccessibilityTraitsStrategy { get } } -public extension AccessibilityItem { +public extension AccessibilityStrategyProvider { var labelStrategy: AccessibilityStrategy { .ignored } var valueStrategy: AccessibilityStrategy { .ignored } var traitsStrategy: AccessibilityTraitsStrategy { .ignored } } -public protocol AccessibilityCell: AccessibilityItem { +public protocol AccessibilityItem: AccessibilityStrategyProvider { typealias AccessibilityModifierType = AccessibilityModifier.Type var modifierType: AccessibilityModifierType { get } - func accessibilityStrategyConflictResolver(cellStrategy: AccessibilityStrategy, generatorStrategy: AccessibilityStrategy?) -> String? + func accessibilityStrategyConflictResolver(itemStrategy: AccessibilityStrategy, generatorStrategy: AccessibilityStrategy?) -> String? } -public extension AccessibilityCell { +public extension AccessibilityItem { var modifierType: AccessibilityModifierType { DefaultAccessibilityModifier.self } - func accessibilityStrategyConflictResolver(cellStrategy: AccessibilityStrategy, generatorStrategy: AccessibilityStrategy?) -> String? { - return [generatorStrategy?.value, cellStrategy.value].compactMap { $0 }.joined(separator: " ") + func accessibilityStrategyConflictResolver(itemStrategy: AccessibilityStrategy, generatorStrategy: AccessibilityStrategy?) -> String? { + return [generatorStrategy?.value, itemStrategy.value].compactMap { $0 }.joined(separator: " ") } } diff --git a/Source/Utils/AccessibilityModifier.swift b/Source/Utils/AccessibilityModifier.swift index 1a3806440..a0a74b60c 100644 --- a/Source/Utils/AccessibilityModifier.swift +++ b/Source/Utils/AccessibilityModifier.swift @@ -9,25 +9,25 @@ import UIKit public protocol AccessibilityModifier { - static func modify(view: UIView, with cell: AccessibilityCell, generator: AccessibilityItem?) + static func modify(view: UIView, with item: AccessibilityItem, generator: AccessibilityStrategyProvider?) } enum DefaultAccessibilityModifier: AccessibilityModifier { - static func modify(view: UIView, with cell: AccessibilityCell, generator: AccessibilityItem?) { + static func modify(view: UIView, with item: AccessibilityItem, generator: AccessibilityStrategyProvider?) { view.isAccessibilityElement = true - if !cell.labelStrategy.isIgnored || generator?.labelStrategy.isIgnored == false { - view.accessibilityLabel = cell.accessibilityStrategyConflictResolver(cellStrategy: cell.labelStrategy, + if !item.labelStrategy.isIgnored || generator?.labelStrategy.isIgnored == false { + view.accessibilityLabel = item.accessibilityStrategyConflictResolver(itemStrategy: item.labelStrategy, generatorStrategy: generator?.labelStrategy) } - if !cell.valueStrategy.isIgnored || generator?.valueStrategy.isIgnored == false { - view.accessibilityValue = cell.accessibilityStrategyConflictResolver(cellStrategy: cell.valueStrategy, + if !item.valueStrategy.isIgnored || generator?.valueStrategy.isIgnored == false { + view.accessibilityValue = item.accessibilityStrategyConflictResolver(itemStrategy: item.valueStrategy, generatorStrategy: generator?.valueStrategy) } - if !cell.traitsStrategy.isIgnored || generator?.traitsStrategy.isIgnored == false { - let traits = [cell.traitsStrategy.value, generator?.traitsStrategy.value] + if !item.traitsStrategy.isIgnored || generator?.traitsStrategy.isIgnored == false { + let traits = [item.traitsStrategy.value, generator?.traitsStrategy.value] .compactMap { $0 } .reduce(UIAccessibilityTraits(), { $0.union($1) }) view.accessibilityTraits = traits From 409472f836391e0e672237ad976713cc667059d6 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Thu, 25 May 2023 13:23:45 +0300 Subject: [PATCH 007/126] fixed collection plugin --- .../CollectionAccessibilityPlugin.swift | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift index e66e4a123..f718e38b9 100644 --- a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift +++ b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift @@ -12,22 +12,33 @@ final class CollectionAccessibilityPlugin: BaseCollectionPlugin override func process(event: CollectionEvent, with manager: BaseCollectionManager?) { switch event { case .willDisplayCell(let indexPath, let cell): - if let accessibilityItem = cell as? AccessibilityItem { - let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityStrategyProvider - accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem, generator: generator) + guard let accessibilityItem = cell as? AccessibilityItem else { + return } + let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityStrategyProvider + accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem, generator: generator) + case .willDisplaySupplementaryView(let indexPath, let view, let kind): - guard [UICollectionView.elementKindSectionHeader, UICollectionView.elementKindSectionFooter].contains(kind) else { + guard let accessibilityItem = view as? AccessibilityItem else { return } - if let accessibilityItem = view as? AccessibilityItem { - let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityStrategyProvider - accessibilityItem.modifierType.modify(view: view, with: accessibilityItem, generator: generator) + + var supplementaryGenerator: AccessibilityStrategyProvider? + switch kind { + case UICollectionView.elementKindSectionHeader: + supplementaryGenerator = manager?.sections[indexPath.section] as? AccessibilityStrategyProvider + case UICollectionView.elementKindSectionFooter: + supplementaryGenerator = manager?.footers[indexPath.section] as? AccessibilityStrategyProvider + default: + break } + accessibilityItem.modifierType.modify(view: view, with: accessibilityItem, generator: supplementaryGenerator) + default: break } } + } extension BaseCollectionPlugin { From fb3db9dfe85e81e92175c4ef4094fe2b7caefa26 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Thu, 25 May 2023 13:46:57 +0300 Subject: [PATCH 008/126] added table accessibility plugin --- Source/Table/Delegate/BaseTableDelegate.swift | 12 ++--- Source/Table/Events/TableEvent.swift | 12 ++--- .../TableAccessibilityPlugin.swift | 48 +++++++++++++++++++ .../PluginAction/TableDisplayablePlugin.swift | 8 ++-- .../TableHeaderVisiblePlugin.swift | 4 +- .../TableLastCellIsVisiblePlugin.swift | 2 +- .../PluginAction/TablePaginatablePlugin.swift | 2 +- Source/Table/TableBuilder.swift | 2 +- 8 files changed, 69 insertions(+), 21 deletions(-) create mode 100644 Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift diff --git a/Source/Table/Delegate/BaseTableDelegate.swift b/Source/Table/Delegate/BaseTableDelegate.swift index c24251749..a6967cdbd 100644 --- a/Source/Table/Delegate/BaseTableDelegate.swift +++ b/Source/Table/Delegate/BaseTableDelegate.swift @@ -92,27 +92,27 @@ extension BaseTableDelegate { extension BaseTableDelegate { open func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { - tablePlugins.process(event: .willDisplayCell(indexPath), with: manager) + tablePlugins.process(event: .willDisplayCell(indexPath, cell), with: manager) } open func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) { - tablePlugins.process(event: .willDisplayHeader(section), with: manager) + tablePlugins.process(event: .willDisplayHeader(section, view), with: manager) } open func tableView(_ tableView: UITableView, didEndDisplayingHeaderView view: UIView, forSection section: Int) { - tablePlugins.process(event: .didEndDisplayHeader(section), with: manager) + tablePlugins.process(event: .didEndDisplayHeader(section, view), with: manager) } open func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) { - tablePlugins.process(event: .didEndDisplayCell(indexPath), with: manager) + tablePlugins.process(event: .didEndDisplayCell(indexPath, cell), with: manager) } open func tableView(_ tableView: UITableView, willDisplayFooterView view: UIView, forSection section: Int) { - tablePlugins.process(event: .willDisplayFooter(section), with: manager) + tablePlugins.process(event: .willDisplayFooter(section, view), with: manager) } open func tableView(_ tableView: UITableView, didEndDisplayingFooterView view: UIView, forSection section: Int) { - tablePlugins.process(event: .didEndDisplayFooter(section), with: manager) + tablePlugins.process(event: .didEndDisplayFooter(section, view), with: manager) } open func tableView(_ tableView: UITableView, canFocusRowAt indexPath: IndexPath) -> Bool { diff --git a/Source/Table/Events/TableEvent.swift b/Source/Table/Events/TableEvent.swift index 22d4f336f..a79995a66 100644 --- a/Source/Table/Events/TableEvent.swift +++ b/Source/Table/Events/TableEvent.swift @@ -18,12 +18,12 @@ public enum TableEvent { case didUnhighlight(IndexPath) case willBeginEditing(IndexPath) case didEndEditing(IndexPath?) - case willDisplayCell(IndexPath) - case didEndDisplayCell(IndexPath) - case willDisplayHeader(Int) - case didEndDisplayHeader(Int) - case willDisplayFooter(Int) - case didEndDisplayFooter(Int) + case willDisplayCell(IndexPath, UITableViewCell) + case didEndDisplayCell(IndexPath, UITableViewCell) + case willDisplayHeader(Int, UIView) + case didEndDisplayHeader(Int, UIView) + case willDisplayFooter(Int, UIView) + case didEndDisplayFooter(Int, UIView) case didUpdateFocus(context: UITableViewFocusUpdateContext, coordinator: UIFocusAnimationCoordinator) case move(from: IndexPath, to: IndexPath) } diff --git a/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift b/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift new file mode 100644 index 000000000..f9e1549c0 --- /dev/null +++ b/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift @@ -0,0 +1,48 @@ +// +// TableAccessibilityPlugin.swift +// ReactiveDataDisplayManager +// +// Created by korshunov on 25.05.2023. +// + +import Foundation + +import UIKit + +final class TableAccessibilityPlugin: BaseTablePlugin { + + override func process(event: TableEvent, with manager: BaseTableManager?) { + switch event { + case .willDisplayCell(let indexPath, let cell): + guard let accessibilityItem = cell as? AccessibilityItem else { + return + } + let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityStrategyProvider + accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem, generator: generator) + + case .willDisplayHeader(let section, let view): + guard let accessibilityItem = view as? AccessibilityItem else { + return + } + let headerGenerator = manager?.sections[section] as? AccessibilityStrategyProvider + accessibilityItem.modifierType.modify(view: view, with: accessibilityItem, generator: headerGenerator) + + case .willDisplayFooter(_, let view): + guard let accessibilityItem = view as? AccessibilityItem else { + return + } + // AccessibilityStrategyProvider for a footer generator is not supported yet + accessibilityItem.modifierType.modify(view: view, with: accessibilityItem, generator: nil) + + default: + break + } + } + +} + +extension BaseTablePlugin { + static func accessibility() -> BaseTablePlugin { + TableAccessibilityPlugin() + } +} diff --git a/Source/Table/Plugins/PluginAction/TableDisplayablePlugin.swift b/Source/Table/Plugins/PluginAction/TableDisplayablePlugin.swift index 62910287c..86fcec6e7 100644 --- a/Source/Table/Plugins/PluginAction/TableDisplayablePlugin.swift +++ b/Source/Table/Plugins/PluginAction/TableDisplayablePlugin.swift @@ -17,16 +17,16 @@ public class TableDisplayablePlugin: BaseTablePlugin { public override func process(event: TableEvent, with manager: BaseTableManager?) { switch event { - case .willDisplayCell(let indexPath): + case .willDisplayCell(let indexPath, _): let displayable = getDisplayableFlowCell(from: manager, at: indexPath) displayable?.willDisplayEvent.invoke(with: ()) - case .didEndDisplayCell(let indexPath): + case .didEndDisplayCell(let indexPath, _): let displayable = getDisplayableFlowCell(from: manager, at: indexPath) displayable?.didEndDisplayEvent.invoke(with: ()) - case .willDisplayHeader(let section): + case .willDisplayHeader(let section, _): let displayable = getDisplayableFlowHeader(from: manager, at: section) displayable?.willDisplayEvent.invoke(with: ()) - case .didEndDisplayHeader(let section): + case .didEndDisplayHeader(let section, _): let displayable = getDisplayableFlowHeader(from: manager, at: section) displayable?.didEndDisplayEvent.invoke(with: ()) default: diff --git a/Source/Table/Plugins/PluginAction/TableHeaderVisiblePlugin.swift b/Source/Table/Plugins/PluginAction/TableHeaderVisiblePlugin.swift index 1dadd2c5b..a86f77799 100644 --- a/Source/Table/Plugins/PluginAction/TableHeaderVisiblePlugin.swift +++ b/Source/Table/Plugins/PluginAction/TableHeaderVisiblePlugin.swift @@ -26,9 +26,9 @@ public class TableHeaderVisiblePlugin: BaseTablePlugin { public override func process(event: TableEvent, with manager: BaseTableManager?) { switch event { - case .willDisplayHeader(let section): + case .willDisplayHeader(let section, _): willDisplayHeader(with: section, manager: manager) - case .didEndDisplayHeader(let section): + case .didEndDisplayHeader(let section, _): didEndDisplayHeader(with: section, manager: manager) default: break diff --git a/Source/Table/Plugins/PluginAction/TableLastCellIsVisiblePlugin.swift b/Source/Table/Plugins/PluginAction/TableLastCellIsVisiblePlugin.swift index 36b0a68d1..fa890f0c1 100644 --- a/Source/Table/Plugins/PluginAction/TableLastCellIsVisiblePlugin.swift +++ b/Source/Table/Plugins/PluginAction/TableLastCellIsVisiblePlugin.swift @@ -27,7 +27,7 @@ public class TableLastCellIsVisiblePlugin: BaseTablePlugin { public override func process(event: TableEvent, with manager: BaseTableManager?) { switch event { - case .willDisplayCell(let indexPath): + case .willDisplayCell(let indexPath, _): guard let generators = manager?.generators else { return } diff --git a/Source/Table/Plugins/PluginAction/TablePaginatablePlugin.swift b/Source/Table/Plugins/PluginAction/TablePaginatablePlugin.swift index a28b07564..882ed622f 100644 --- a/Source/Table/Plugins/PluginAction/TablePaginatablePlugin.swift +++ b/Source/Table/Plugins/PluginAction/TablePaginatablePlugin.swift @@ -123,7 +123,7 @@ public class TablePaginatablePlugin: BaseTablePlugin { public override func process(event: TableEvent, with manager: BaseTableManager?) { switch event { - case .willDisplayCell(let indexPath): + case .willDisplayCell(let indexPath, _): guard let generators = manager?.generators else { return } diff --git a/Source/Table/TableBuilder.swift b/Source/Table/TableBuilder.swift index f09ab7cd8..61660e3b6 100644 --- a/Source/Table/TableBuilder.swift +++ b/Source/Table/TableBuilder.swift @@ -143,7 +143,7 @@ public class TableBuilder { /// Build delegate, dataSource, view and data display manager together and returns DataDisplayManager public func build() -> T { - + tablePlugins.add(.accessibility()) animator = QueuedAnimator(baseAnimator: TableSafeAnimator(baseAnimator: animator, generatorsProvider: manager), debounceTime: emptyAnimatorDebounceTime) From daa29db267631e12d8a078555f836ed4106da981 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Thu, 25 May 2023 16:24:42 +0300 Subject: [PATCH 009/126] added traits provider for base protocols --- .../Generators/BaseCollectionCellGenerator.swift | 3 +-- Source/Collection/Protocols/CollectionFoldableItem.swift | 6 +++++- Source/Protocols/Plugins/Generators/FoldableItem.swift | 4 +++- Source/Protocols/Plugins/Generators/SelectableItem.swift | 6 +++++- Source/Protocols/Protocols.swift | 8 ++++++-- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Source/Collection/Generators/BaseCollectionCellGenerator.swift b/Source/Collection/Generators/BaseCollectionCellGenerator.swift index 7fcf1a093..9982176c6 100644 --- a/Source/Collection/Generators/BaseCollectionCellGenerator.swift +++ b/Source/Collection/Generators/BaseCollectionCellGenerator.swift @@ -8,7 +8,7 @@ import UIKit -open class BaseCollectionCellGenerator: SelectableItem, AccessibilityItem where Cell: UICollectionViewCell { +open class BaseCollectionCellGenerator: SelectableItem where Cell: UICollectionViewCell { // MARK: - Public Properties @@ -16,7 +16,6 @@ open class BaseCollectionCellGenerator: SelectableItem, public var didSelectEvent = BaseEvent() public var didDeselectEvent = BaseEvent() public let model: Cell.Model - public var traitsStrategy: AccessibilityTraitsStrategy = .just(.button) // MARK: - Private Properties diff --git a/Source/Collection/Protocols/CollectionFoldableItem.swift b/Source/Collection/Protocols/CollectionFoldableItem.swift index 6146a89d5..508aa780d 100644 --- a/Source/Collection/Protocols/CollectionFoldableItem.swift +++ b/Source/Collection/Protocols/CollectionFoldableItem.swift @@ -7,8 +7,12 @@ // // sourcery: AutoMockable -public protocol CollectionFoldableItem: AnyObject { +public protocol CollectionFoldableItem: AnyObject, AccessibilityStrategyProvider { var didFoldEvent: BaseEvent { get } var isExpanded: Bool { get set } var childGenerators: [CollectionCellGenerator] { get set } } + +extension CollectionFoldableItem { + public var traitsStrategy: AccessibilityTraitsStrategy { .just(.button) } +} diff --git a/Source/Protocols/Plugins/Generators/FoldableItem.swift b/Source/Protocols/Plugins/Generators/FoldableItem.swift index 556fc2e6e..a287c1953 100644 --- a/Source/Protocols/Plugins/Generators/FoldableItem.swift +++ b/Source/Protocols/Plugins/Generators/FoldableItem.swift @@ -14,7 +14,7 @@ public protocol FoldableStateHolder: AnyObject { } /// Protocol for `TableCellGenerator` to manage expand/collapse state -public protocol FoldableItem: AnyObject { +public protocol FoldableItem: AnyObject, AccessibilityStrategyProvider { /// Set animation for folder's expand / shrink var animation: TableFoldablePlugin.AnimationGroup { get } @@ -36,4 +36,6 @@ public extension FoldableItem { return (.none, .fade) } + var traitsStrategy: AccessibilityTraitsStrategy { .just(.button) } + } diff --git a/Source/Protocols/Plugins/Generators/SelectableItem.swift b/Source/Protocols/Plugins/Generators/SelectableItem.swift index 8d74841b0..e0307c74a 100644 --- a/Source/Protocols/Plugins/Generators/SelectableItem.swift +++ b/Source/Protocols/Plugins/Generators/SelectableItem.swift @@ -9,7 +9,7 @@ import UIKit /// Protocol for `Generator` to handle didSelect event -public protocol SelectableItem: AnyObject { +public protocol SelectableItem: AnyObject, AccessibilityStrategyProvider { /// Invokes when user taps on the item. Requires .selectable plugin!!! var didSelectEvent: BaseEvent { get } @@ -22,3 +22,7 @@ public protocol SelectableItem: AnyObject { /// immediately after tap. If you set it to **false**, they don't deselect. var isNeedDeselect: Bool { get set } } + +extension SelectableItem { + public var traitsStrategy: AccessibilityTraitsStrategy { .just(.button) } +} diff --git a/Source/Protocols/Protocols.swift b/Source/Protocols/Protocols.swift index 04da18410..6750ace76 100644 --- a/Source/Protocols/Protocols.swift +++ b/Source/Protocols/Protocols.swift @@ -9,10 +9,12 @@ import UIKit // sourcery: AutoMockable -open class TableHeaderGenerator: ViewGenerator { +open class TableHeaderGenerator: ViewGenerator, AccessibilityStrategyProvider { public let uuid = UUID().uuidString + open var traitsStrategy: AccessibilityTraitsStrategy { .just(.header) } + public init() { } open func generate() -> UIView { @@ -68,7 +70,7 @@ public extension TableCellGenerator { // sourcery: AutoMockable /// Protocol that incapsulated type of Header -public protocol CollectionHeaderGenerator: AnyObject { +public protocol CollectionHeaderGenerator: AnyObject, AccessibilityStrategyProvider { var identifier: UICollectionReusableView.Type { get } @@ -90,6 +92,8 @@ public extension CollectionHeaderGenerator { return nil } + var traitsStrategy: AccessibilityTraitsStrategy { .just(.header) } + } // sourcery: AutoMockable From 5bf16b78277cc2f7b2fa19992d62a9da212f170a Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Thu, 25 May 2023 16:35:27 +0300 Subject: [PATCH 010/126] fixed swiftlint --- Source/Utils/AccessibilityModifier.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Utils/AccessibilityModifier.swift b/Source/Utils/AccessibilityModifier.swift index a0a74b60c..3ed1e8dd5 100644 --- a/Source/Utils/AccessibilityModifier.swift +++ b/Source/Utils/AccessibilityModifier.swift @@ -7,7 +7,6 @@ import UIKit - public protocol AccessibilityModifier { static func modify(view: UIView, with item: AccessibilityItem, generator: AccessibilityStrategyProvider?) } From 63beef13bfb06bc25d605d7b5fb813467d5aa1ac Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Thu, 25 May 2023 16:35:59 +0300 Subject: [PATCH 011/126] added some example --- .../TitleCollectionListCell/TitleCollectionListCell.swift | 7 ++++++- .../HeaderCollectionListView.swift | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionListCell/TitleCollectionListCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionListCell/TitleCollectionListCell.swift index bf422de97..b45ecd9ea 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionListCell/TitleCollectionListCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionListCell/TitleCollectionListCell.swift @@ -9,12 +9,17 @@ import UIKit import ReactiveDataDisplayManager -class TitleCollectionListCell: UICollectionViewCell, ConfigurableItem { +class TitleCollectionListCell: UICollectionViewCell, ConfigurableItem, AccessibilityItem { // MARK: - IBOutlets @IBOutlet private weak var titleLabel: UILabel! + // MARK: - AccessibilityItem + + var labelStrategy: AccessibilityStrategy { .from(titleLabel) } + var traitsStrategy: AccessibilityTraitsStrategy { .from(titleLabel) } + // MARK: - Configurable func configure(with title: String) { diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/HeaderCollectionListView/HeaderCollectionListView.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/HeaderCollectionListView/HeaderCollectionListView.swift index ea7574582..c8cbdda3b 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/HeaderCollectionListView/HeaderCollectionListView.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/HeaderCollectionListView/HeaderCollectionListView.swift @@ -7,13 +7,19 @@ // import UIKit +import ReactiveDataDisplayManager -final class HeaderCollectionListView: UICollectionReusableView { +final class HeaderCollectionListView: UICollectionReusableView, AccessibilityItem { // MARK: - IBOutlets @IBOutlet private weak var titleLabel: UILabel! + // MARK: - AccessibilityItem + + var labelStrategy: AccessibilityStrategy { .from(titleLabel) } + var traitsStrategy: AccessibilityTraitsStrategy { .from(titleLabel) } + // MARK: - Internal Methods func fill(title: String) { From 81beafa1c5dbbce0db07d8a7c4f63b882d3e0832 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Fri, 26 May 2023 12:59:50 +0300 Subject: [PATCH 012/126] added comments --- .../Generators/AccessibilityItem.swift | 44 ++++++++++++++++++- Source/Utils/AccessibilityModifier.swift | 2 + 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/Source/Protocols/Plugins/Generators/AccessibilityItem.swift b/Source/Protocols/Plugins/Generators/AccessibilityItem.swift index 91f4cd90e..bcbf5999f 100644 --- a/Source/Protocols/Plugins/Generators/AccessibilityItem.swift +++ b/Source/Protocols/Plugins/Generators/AccessibilityItem.swift @@ -7,10 +7,20 @@ import UIKit +/// Default accessibility strategy for string value public enum AccessibilityStrategy { + + /// value will not be change by this strategy case ignored + + /// simple string value, can be nil case just(String?) + + /// a reference value from object. By default it's `accessibilityLabel` + /// - parameter keyPath: specified keypath from value would be taken case from(NSObject, keyPath: KeyPath = \.accessibilityLabel) + + /// a combination of strategies, joined by provided separator indirect case joined([AccessibilityStrategy], separator: String) var isIgnored: Bool { @@ -35,10 +45,19 @@ public enum AccessibilityStrategy { } } +/// Accessibility strategy for `UIAccessibilityTraits` parameter public enum AccessibilityTraitsStrategy { + + /// value will not be change by this strategy case ignored + + /// simple accessibility traits case just(UIAccessibilityTraits) + + /// a reference traits from another object case from(NSObject) + + /// a reference traits merged from another objects case merge([NSObject]) var isIgnored: Bool { @@ -49,6 +68,8 @@ public enum AccessibilityTraitsStrategy { } } + /// accessibility traits of current strategy + /// - Returns: nil if `isIgnored`, otherwise `UIAccessibilityTraits` var value: UIAccessibilityTraits? { switch self { case .ignored: @@ -63,9 +84,16 @@ public enum AccessibilityTraitsStrategy { } } +/// Accessibility strategies provider protocol for generators public protocol AccessibilityStrategyProvider { + + /// strategy for `accessibilityLabel`. Default: `.ignored` var labelStrategy: AccessibilityStrategy { get } + + /// strategy for `accessibilityValue`. Default: `.ignored` var valueStrategy: AccessibilityStrategy { get } + + /// strategy for `accessibilityTraits`. Default: `.ignored` var traitsStrategy: AccessibilityTraitsStrategy { get } } @@ -75,10 +103,22 @@ public extension AccessibilityStrategyProvider { var traitsStrategy: AccessibilityTraitsStrategy { .ignored } } -public protocol AccessibilityItem: AccessibilityStrategyProvider { +/// Protocol for cells +public protocol AccessibilityItem: UIView, AccessibilityStrategyProvider { typealias AccessibilityModifierType = AccessibilityModifier.Type + + /// Type of modifier that will be used to apply strategies to view. Default it's internal implementation + /// + /// You can provide your own type of `AccessibilityModifier` implementation to change the way how stategies applied to view. + /// Also `AccessibilityStrategyProvider` can be extended by your protocol to add new parameters. And to apply new parameters you need to provide a custom modifier var modifierType: AccessibilityModifierType { get } + /// Conficts resolver when generator and item contains `AccessibilityStrategy`. By default values will be joined with a space separator in next order: generator, item + /// + /// You can define your own implementation to change separator or order of values. + /// - parameter itemStrategy: strategy defined in cell + /// - parameter generatorStrategy: stategy provided from cell's generator. Often this is `nil` or `.ignored` + /// - returns: value combined from both strategies func accessibilityStrategyConflictResolver(itemStrategy: AccessibilityStrategy, generatorStrategy: AccessibilityStrategy?) -> String? } @@ -86,6 +126,6 @@ public extension AccessibilityItem { var modifierType: AccessibilityModifierType { DefaultAccessibilityModifier.self } func accessibilityStrategyConflictResolver(itemStrategy: AccessibilityStrategy, generatorStrategy: AccessibilityStrategy?) -> String? { - return [generatorStrategy?.value, itemStrategy.value].compactMap { $0 }.joined(separator: " ") + return [generatorStrategy, itemStrategy].compactMap(\.?.value).joined(separator: " ") } } diff --git a/Source/Utils/AccessibilityModifier.swift b/Source/Utils/AccessibilityModifier.swift index 3ed1e8dd5..88ca76d70 100644 --- a/Source/Utils/AccessibilityModifier.swift +++ b/Source/Utils/AccessibilityModifier.swift @@ -7,7 +7,9 @@ import UIKit +/// Protocol for accessibility parameters modifier public protocol AccessibilityModifier { + /// Modifies the view with strategies from item and generator static func modify(view: UIView, with item: AccessibilityItem, generator: AccessibilityStrategyProvider?) } From 13e233fb2c940ba528bb9b4a803f264d1502e2f0 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Tue, 30 May 2023 13:43:31 +0300 Subject: [PATCH 013/126] update hooks --- hooks/post-checkout | 1 + hooks/post-merge | 1 + 2 files changed, 2 insertions(+) diff --git a/hooks/post-checkout b/hooks/post-checkout index b933c5391..22db12c8e 100755 --- a/hooks/post-checkout +++ b/hooks/post-checkout @@ -1,3 +1,4 @@ #!/bin/bash +source ~/.bash_profile make projects diff --git a/hooks/post-merge b/hooks/post-merge index b933c5391..481e85ccc 100755 --- a/hooks/post-merge +++ b/hooks/post-merge @@ -1,3 +1,4 @@ #!/bin/bash +source ~/.bash_profile make projects From e7fcce1f7b36244c00393481fe337cfd593484aa Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Tue, 30 May 2023 15:22:55 +0300 Subject: [PATCH 014/126] accessibility improvements --- .../CollectionAccessibilityPlugin.swift | 14 ++++++-- .../Generators/AccessibilityItem.swift | 34 +++++++++++------- .../TableAccessibilityPlugin.swift | 17 ++++++--- Source/Utils/AccessibilityModifier.swift | 35 ++++++++++++++----- 4 files changed, 70 insertions(+), 30 deletions(-) diff --git a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift index f718e38b9..2c29a01e7 100644 --- a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift +++ b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift @@ -15,8 +15,11 @@ final class CollectionAccessibilityPlugin: BaseCollectionPlugin guard let accessibilityItem = cell as? AccessibilityItem else { return } - let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityStrategyProvider - accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem, generator: generator) + if let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityStrategyProvider { + accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem, generator: generator) + } else { + accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem) + } case .willDisplaySupplementaryView(let indexPath, let view, let kind): guard let accessibilityItem = view as? AccessibilityItem else { @@ -32,7 +35,12 @@ final class CollectionAccessibilityPlugin: BaseCollectionPlugin default: break } - accessibilityItem.modifierType.modify(view: view, with: accessibilityItem, generator: supplementaryGenerator) + if let supplementaryGenerator { + accessibilityItem.modifierType.modify(view: view, with: accessibilityItem, generator: supplementaryGenerator) + } else { + accessibilityItem.modifierType.modify(view: view, with: accessibilityItem) + } + default: break diff --git a/Source/Protocols/Plugins/Generators/AccessibilityItem.swift b/Source/Protocols/Plugins/Generators/AccessibilityItem.swift index bcbf5999f..a4e91027a 100644 --- a/Source/Protocols/Plugins/Generators/AccessibilityItem.swift +++ b/Source/Protocols/Plugins/Generators/AccessibilityItem.swift @@ -8,7 +8,7 @@ import UIKit /// Default accessibility strategy for string value -public enum AccessibilityStrategy { +public enum AccessibilityStringStrategy { /// value will not be change by this strategy case ignored @@ -18,10 +18,10 @@ public enum AccessibilityStrategy { /// a reference value from object. By default it's `accessibilityLabel` /// - parameter keyPath: specified keypath from value would be taken - case from(NSObject, keyPath: KeyPath = \.accessibilityLabel) + case from(object: NSObject, keyPath: KeyPath = \.accessibilityLabel) /// a combination of strategies, joined by provided separator - indirect case joined([AccessibilityStrategy], separator: String) + indirect case joined([AccessibilityStringStrategy], separator: String) var isIgnored: Bool { if case .ignored = self { @@ -55,7 +55,7 @@ public enum AccessibilityTraitsStrategy { case just(UIAccessibilityTraits) /// a reference traits from another object - case from(NSObject) + case from(object: NSObject) /// a reference traits merged from another objects case merge([NSObject]) @@ -88,19 +88,25 @@ public enum AccessibilityTraitsStrategy { public protocol AccessibilityStrategyProvider { /// strategy for `accessibilityLabel`. Default: `.ignored` - var labelStrategy: AccessibilityStrategy { get } + var labelStrategy: AccessibilityStringStrategy { get } /// strategy for `accessibilityValue`. Default: `.ignored` - var valueStrategy: AccessibilityStrategy { get } + var valueStrategy: AccessibilityStringStrategy { get } /// strategy for `accessibilityTraits`. Default: `.ignored` var traitsStrategy: AccessibilityTraitsStrategy { get } + + /// Idicates that `AccessibilityItem` should become an accessibility element. Equals `true` if all strategies is in state `.ignored` + var isAccessibilityIgnored: Bool { get } } public extension AccessibilityStrategyProvider { - var labelStrategy: AccessibilityStrategy { .ignored } - var valueStrategy: AccessibilityStrategy { .ignored } + var labelStrategy: AccessibilityStringStrategy { .ignored } + var valueStrategy: AccessibilityStringStrategy { .ignored } var traitsStrategy: AccessibilityTraitsStrategy { .ignored } + var isAccessibilityIgnored: Bool { + return [labelStrategy.isIgnored, valueStrategy.isIgnored, traitsStrategy.isIgnored].allSatisfy { $0 } + } } /// Protocol for cells @@ -113,19 +119,21 @@ public protocol AccessibilityItem: UIView, AccessibilityStrategyProvider { /// Also `AccessibilityStrategyProvider` can be extended by your protocol to add new parameters. And to apply new parameters you need to provide a custom modifier var modifierType: AccessibilityModifierType { get } - /// Conficts resolver when generator and item contains `AccessibilityStrategy`. By default values will be joined with a space separator in next order: generator, item + /// Conficts resolver when generator and item contains `AccessibilityStringStrategy`. By default values will be joined with a space separator in next order: generator, item /// /// You can define your own implementation to change separator or order of values. /// - parameter itemStrategy: strategy defined in cell - /// - parameter generatorStrategy: stategy provided from cell's generator. Often this is `nil` or `.ignored` + /// - parameter generatorStrategy: stategy provided from cell's generator /// - returns: value combined from both strategies - func accessibilityStrategyConflictResolver(itemStrategy: AccessibilityStrategy, generatorStrategy: AccessibilityStrategy?) -> String? + func accessibilityStrategyConflictResolver(itemStrategy: AccessibilityStringStrategy, + generatorStrategy: AccessibilityStringStrategy) -> String? } public extension AccessibilityItem { var modifierType: AccessibilityModifierType { DefaultAccessibilityModifier.self } - func accessibilityStrategyConflictResolver(itemStrategy: AccessibilityStrategy, generatorStrategy: AccessibilityStrategy?) -> String? { - return [generatorStrategy, itemStrategy].compactMap(\.?.value).joined(separator: " ") + func accessibilityStrategyConflictResolver(itemStrategy: AccessibilityStringStrategy, + generatorStrategy: AccessibilityStringStrategy) -> String? { + return [generatorStrategy, itemStrategy].compactMap(\.value).joined(separator: " ") } } diff --git a/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift b/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift index f9e1549c0..fb65a2c14 100644 --- a/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift +++ b/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift @@ -17,22 +17,29 @@ final class TableAccessibilityPlugin: BaseTablePlugin { guard let accessibilityItem = cell as? AccessibilityItem else { return } - let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityStrategyProvider - accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem, generator: generator) + if let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityStrategyProvider { + accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem, generator: generator) + } else { + accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem) + } case .willDisplayHeader(let section, let view): guard let accessibilityItem = view as? AccessibilityItem else { return } - let headerGenerator = manager?.sections[section] as? AccessibilityStrategyProvider - accessibilityItem.modifierType.modify(view: view, with: accessibilityItem, generator: headerGenerator) + if let headerGenerator = manager?.sections[section] as? AccessibilityStrategyProvider { + accessibilityItem.modifierType.modify(view: view, with: accessibilityItem, generator: headerGenerator) + } else { + accessibilityItem.modifierType.modify(view: view, with: accessibilityItem) + } case .willDisplayFooter(_, let view): guard let accessibilityItem = view as? AccessibilityItem else { return } // AccessibilityStrategyProvider for a footer generator is not supported yet - accessibilityItem.modifierType.modify(view: view, with: accessibilityItem, generator: nil) + // TODO: SPT-1468 + accessibilityItem.modifierType.modify(view: view, with: accessibilityItem) default: break diff --git a/Source/Utils/AccessibilityModifier.swift b/Source/Utils/AccessibilityModifier.swift index 88ca76d70..31da1a1c2 100644 --- a/Source/Utils/AccessibilityModifier.swift +++ b/Source/Utils/AccessibilityModifier.swift @@ -9,26 +9,43 @@ import UIKit /// Protocol for accessibility parameters modifier public protocol AccessibilityModifier { - /// Modifies the view with strategies from item and generator - static func modify(view: UIView, with item: AccessibilityItem, generator: AccessibilityStrategyProvider?) + /// Modifies the view with strategies from `AccessibilityItem` + static func modify(view: UIView, with item: AccessibilityItem) + + /// Modifies the view with strategies from `AccessibilityItem` and `AccessibilityStrategyProvider` + static func modify(view: UIView, with item: AccessibilityItem, generator: AccessibilityStrategyProvider) } enum DefaultAccessibilityModifier: AccessibilityModifier { - static func modify(view: UIView, with item: AccessibilityItem, generator: AccessibilityStrategyProvider?) { + + static func modify(view: UIView, with item: AccessibilityItem) { + guard !item.isAccessibilityIgnored else { + return + } + view.isAccessibilityElement = true + view.accessibilityLabel = item.labelStrategy.value + view.accessibilityValue = item.valueStrategy.value + view.accessibilityTraits = item.traitsStrategy.value ?? .none + } + + static func modify(view: UIView, with item: AccessibilityItem, generator: AccessibilityStrategyProvider) { + guard !item.isAccessibilityIgnored || !generator.isAccessibilityIgnored else { + return + } view.isAccessibilityElement = true - if !item.labelStrategy.isIgnored || generator?.labelStrategy.isIgnored == false { + if !item.labelStrategy.isIgnored || !generator.labelStrategy.isIgnored { view.accessibilityLabel = item.accessibilityStrategyConflictResolver(itemStrategy: item.labelStrategy, - generatorStrategy: generator?.labelStrategy) + generatorStrategy: generator.labelStrategy) } - if !item.valueStrategy.isIgnored || generator?.valueStrategy.isIgnored == false { + if !item.valueStrategy.isIgnored || !generator.valueStrategy.isIgnored { view.accessibilityValue = item.accessibilityStrategyConflictResolver(itemStrategy: item.valueStrategy, - generatorStrategy: generator?.valueStrategy) + generatorStrategy: generator.valueStrategy) } - if !item.traitsStrategy.isIgnored || generator?.traitsStrategy.isIgnored == false { - let traits = [item.traitsStrategy.value, generator?.traitsStrategy.value] + if !item.traitsStrategy.isIgnored || !generator.traitsStrategy.isIgnored { + let traits = [item.traitsStrategy.value, generator.traitsStrategy.value] .compactMap { $0 } .reduce(UIAccessibilityTraits(), { $0.union($1) }) view.accessibilityTraits = traits From 69b1c9273b2d753004c192ee0c98458bd9b9bf13 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Tue, 30 May 2023 16:33:26 +0300 Subject: [PATCH 015/126] fixed modifier methods --- .../CollectionAccessibilityPlugin.swift | 8 ++-- .../TableAccessibilityPlugin.swift | 10 ++--- Source/Utils/AccessibilityModifier.swift | 40 ++++++++++--------- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift index 2c29a01e7..d92843d3a 100644 --- a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift +++ b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift @@ -16,9 +16,9 @@ final class CollectionAccessibilityPlugin: BaseCollectionPlugin return } if let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityStrategyProvider { - accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem, generator: generator) + accessibilityItem.modifierType.modify(item: accessibilityItem, generator: generator) } else { - accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem) + accessibilityItem.modifierType.modify(item: accessibilityItem) } case .willDisplaySupplementaryView(let indexPath, let view, let kind): @@ -36,9 +36,9 @@ final class CollectionAccessibilityPlugin: BaseCollectionPlugin break } if let supplementaryGenerator { - accessibilityItem.modifierType.modify(view: view, with: accessibilityItem, generator: supplementaryGenerator) + accessibilityItem.modifierType.modify(item: accessibilityItem, generator: supplementaryGenerator) } else { - accessibilityItem.modifierType.modify(view: view, with: accessibilityItem) + accessibilityItem.modifierType.modify(item: accessibilityItem) } diff --git a/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift b/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift index fb65a2c14..33025a0c3 100644 --- a/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift +++ b/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift @@ -18,9 +18,9 @@ final class TableAccessibilityPlugin: BaseTablePlugin { return } if let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityStrategyProvider { - accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem, generator: generator) + accessibilityItem.modifierType.modify(item: accessibilityItem, generator: generator) } else { - accessibilityItem.modifierType.modify(view: cell.contentView, with: accessibilityItem) + accessibilityItem.modifierType.modify(item: accessibilityItem) } case .willDisplayHeader(let section, let view): @@ -28,9 +28,9 @@ final class TableAccessibilityPlugin: BaseTablePlugin { return } if let headerGenerator = manager?.sections[section] as? AccessibilityStrategyProvider { - accessibilityItem.modifierType.modify(view: view, with: accessibilityItem, generator: headerGenerator) + accessibilityItem.modifierType.modify(item: accessibilityItem, generator: headerGenerator) } else { - accessibilityItem.modifierType.modify(view: view, with: accessibilityItem) + accessibilityItem.modifierType.modify(item: accessibilityItem) } case .willDisplayFooter(_, let view): @@ -39,7 +39,7 @@ final class TableAccessibilityPlugin: BaseTablePlugin { } // AccessibilityStrategyProvider for a footer generator is not supported yet // TODO: SPT-1468 - accessibilityItem.modifierType.modify(view: view, with: accessibilityItem) + accessibilityItem.modifierType.modify(item: accessibilityItem) default: break diff --git a/Source/Utils/AccessibilityModifier.swift b/Source/Utils/AccessibilityModifier.swift index 31da1a1c2..0ce7a6d72 100644 --- a/Source/Utils/AccessibilityModifier.swift +++ b/Source/Utils/AccessibilityModifier.swift @@ -9,38 +9,40 @@ import UIKit /// Protocol for accessibility parameters modifier public protocol AccessibilityModifier { - /// Modifies the view with strategies from `AccessibilityItem` - static func modify(view: UIView, with item: AccessibilityItem) + /// Modifies`AccessibilityItem` with provided strategies from + static func modify(item: AccessibilityItem) - /// Modifies the view with strategies from `AccessibilityItem` and `AccessibilityStrategyProvider` - static func modify(view: UIView, with item: AccessibilityItem, generator: AccessibilityStrategyProvider) + /// Modifies `AccessibilityItem` with provided strategies and `AccessibilityStrategyProvider` + static func modify(item: AccessibilityItem, generator: AccessibilityStrategyProvider) } enum DefaultAccessibilityModifier: AccessibilityModifier { + static func modify(item: AccessibilityItem) { + item.isAccessibilityElement = !item.isAccessibilityIgnored - static func modify(view: UIView, with item: AccessibilityItem) { - guard !item.isAccessibilityIgnored else { - return + if !item.labelStrategy.isIgnored { + item.accessibilityLabel = item.labelStrategy.value + } + + if !item.valueStrategy.isIgnored { + item.accessibilityValue = item.valueStrategy.value } - view.isAccessibilityElement = true - view.accessibilityLabel = item.labelStrategy.value - view.accessibilityValue = item.valueStrategy.value - view.accessibilityTraits = item.traitsStrategy.value ?? .none - } - static func modify(view: UIView, with item: AccessibilityItem, generator: AccessibilityStrategyProvider) { - guard !item.isAccessibilityIgnored || !generator.isAccessibilityIgnored else { - return + if !item.traitsStrategy.isIgnored, let traits = item.traitsStrategy.value { + item.accessibilityTraits = traits } - view.isAccessibilityElement = true + } + + static func modify(item: AccessibilityItem, generator: AccessibilityStrategyProvider) { + item.isAccessibilityElement = !item.isAccessibilityIgnored || !generator.isAccessibilityIgnored if !item.labelStrategy.isIgnored || !generator.labelStrategy.isIgnored { - view.accessibilityLabel = item.accessibilityStrategyConflictResolver(itemStrategy: item.labelStrategy, + item.accessibilityLabel = item.accessibilityStrategyConflictResolver(itemStrategy: item.labelStrategy, generatorStrategy: generator.labelStrategy) } if !item.valueStrategy.isIgnored || !generator.valueStrategy.isIgnored { - view.accessibilityValue = item.accessibilityStrategyConflictResolver(itemStrategy: item.valueStrategy, + item.accessibilityValue = item.accessibilityStrategyConflictResolver(itemStrategy: item.valueStrategy, generatorStrategy: generator.valueStrategy) } @@ -48,7 +50,7 @@ enum DefaultAccessibilityModifier: AccessibilityModifier { let traits = [item.traitsStrategy.value, generator.traitsStrategy.value] .compactMap { $0 } .reduce(UIAccessibilityTraits(), { $0.union($1) }) - view.accessibilityTraits = traits + item.accessibilityTraits = traits } } } From 107aaad373362b780f87b3c76cbb2c36ddc464d4 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Tue, 30 May 2023 16:41:01 +0300 Subject: [PATCH 016/126] made default accessibility modifier public --- Source/Utils/AccessibilityModifier.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Utils/AccessibilityModifier.swift b/Source/Utils/AccessibilityModifier.swift index 0ce7a6d72..7e81bdfca 100644 --- a/Source/Utils/AccessibilityModifier.swift +++ b/Source/Utils/AccessibilityModifier.swift @@ -16,8 +16,8 @@ public protocol AccessibilityModifier { static func modify(item: AccessibilityItem, generator: AccessibilityStrategyProvider) } -enum DefaultAccessibilityModifier: AccessibilityModifier { - static func modify(item: AccessibilityItem) { +public enum DefaultAccessibilityModifier: AccessibilityModifier { + static public func modify(item: AccessibilityItem) { item.isAccessibilityElement = !item.isAccessibilityIgnored if !item.labelStrategy.isIgnored { @@ -33,7 +33,7 @@ enum DefaultAccessibilityModifier: AccessibilityModifier { } } - static func modify(item: AccessibilityItem, generator: AccessibilityStrategyProvider) { + static public func modify(item: AccessibilityItem, generator: AccessibilityStrategyProvider) { item.isAccessibilityElement = !item.isAccessibilityIgnored || !generator.isAccessibilityIgnored if !item.labelStrategy.isIgnored || !generator.labelStrategy.isIgnored { From ca8ff8b1266d4d23b2b1a3ba095bb4d5e5ab4560 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Tue, 30 May 2023 16:47:56 +0300 Subject: [PATCH 017/126] fixed modifier --- Source/Utils/AccessibilityModifier.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Source/Utils/AccessibilityModifier.swift b/Source/Utils/AccessibilityModifier.swift index 7e81bdfca..2aacf6f9b 100644 --- a/Source/Utils/AccessibilityModifier.swift +++ b/Source/Utils/AccessibilityModifier.swift @@ -12,13 +12,16 @@ public protocol AccessibilityModifier { /// Modifies`AccessibilityItem` with provided strategies from static func modify(item: AccessibilityItem) - /// Modifies `AccessibilityItem` with provided strategies and `AccessibilityStrategyProvider` + /// Modifies `AccessibilityItem` with provided strategies and `AccessibilityStrategyProvider` static func modify(item: AccessibilityItem, generator: AccessibilityStrategyProvider) } public enum DefaultAccessibilityModifier: AccessibilityModifier { static public func modify(item: AccessibilityItem) { - item.isAccessibilityElement = !item.isAccessibilityIgnored + guard !item.isAccessibilityIgnored else { + return + } + item.isAccessibilityElement = true if !item.labelStrategy.isIgnored { item.accessibilityLabel = item.labelStrategy.value @@ -34,7 +37,10 @@ public enum DefaultAccessibilityModifier: AccessibilityModifier { } static public func modify(item: AccessibilityItem, generator: AccessibilityStrategyProvider) { - item.isAccessibilityElement = !item.isAccessibilityIgnored || !generator.isAccessibilityIgnored + guard !item.isAccessibilityIgnored || !generator.isAccessibilityIgnored else { + return + } + item.isAccessibilityElement = true if !item.labelStrategy.isIgnored || !generator.labelStrategy.isIgnored { item.accessibilityLabel = item.accessibilityStrategyConflictResolver(itemStrategy: item.labelStrategy, From d9fc358ca5b18f000495e633a2facc2355b5e854 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Tue, 30 May 2023 17:51:36 +0300 Subject: [PATCH 018/126] fixed tests --- ...nt+Expectations.swift => XCUIElement+Extensions.swift} | 6 +++++- .../Plugins/HighlightablePluginExampleUITest.swift | 8 ++++---- 2 files changed, 9 insertions(+), 5 deletions(-) rename Example/ReactiveDataDisplayManagerExampleUITests/BaseClasses/{XCUIElement+Expectations.swift => XCUIElement+Extensions.swift} (83%) diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/BaseClasses/XCUIElement+Expectations.swift b/Example/ReactiveDataDisplayManagerExampleUITests/BaseClasses/XCUIElement+Extensions.swift similarity index 83% rename from Example/ReactiveDataDisplayManagerExampleUITests/BaseClasses/XCUIElement+Expectations.swift rename to Example/ReactiveDataDisplayManagerExampleUITests/BaseClasses/XCUIElement+Extensions.swift index af4baa579..636d3a0c7 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/BaseClasses/XCUIElement+Expectations.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/BaseClasses/XCUIElement+Extensions.swift @@ -1,5 +1,5 @@ // -// XCUIElement+Expectations.swift +// XCUIElement+Extensions.swift // ReactiveDataDisplayManagerExampleUITests // // Created by Никита Коробейников on 19.12.2022. @@ -9,6 +9,10 @@ import XCTest extension XCUIElement { + var stringValue: String? { + value as? String + } + func waitForNonExistence(timeout: TimeInterval) -> Bool { let predicate = NSPredicate(format: "exists == false") let expectation = XCTNSPredicateExpectation(predicate: predicate, object: self) diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/HighlightablePluginExampleUITest.swift b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/HighlightablePluginExampleUITest.swift index 94294a237..b3f767c8f 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/HighlightablePluginExampleUITest.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/HighlightablePluginExampleUITest.swift @@ -64,14 +64,14 @@ final class HighlightablePluginExampleUITest: BaseUITestCase { let cell = getFirstCell(for: .collection, id: "Collection_with_selectable_cells") DispatchQueue.main.asyncAfter(deadline: .now() + timeout) { - wasPresed = cell.label == highlightedStyle + wasPresed = cell.stringValue == highlightedStyle expectation.fulfill() } cell.press(forDuration: 2) wait(for: [expectation], timeout: timeout) XCTAssertTrue(wasPresed) - XCTAssertEqual(cell.label, normalStyle) + XCTAssertEqual(cell.stringValue, normalStyle) } func testCollection_whenCellTaped_thenTurnedSelectStyleAndDeselectStyle() throws { @@ -85,11 +85,11 @@ final class HighlightablePluginExampleUITest: BaseUITestCase { let cell = getFirstCell(for: .collection, id: "Collection_with_selectable_cells") cell.tap() - XCTAssertEqual(cell.label, selectedStyle) + XCTAssertEqual(cell.stringValue, selectedStyle) XCTAssertTrue(cell.isSelected) cell.tap() - XCTAssertEqual(cell.label, normalStyle) + XCTAssertEqual(cell.stringValue, normalStyle) XCTAssertFalse(cell.isSelected) } From d28f1a154801585976726cbad30c44e348aa197f Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Tue, 30 May 2023 18:02:55 +0300 Subject: [PATCH 019/126] implemented accessibility for collection views --- .../RectangleColorCollectionViewCell.swift | 11 ++++++++++- ...ittingCompressedSizeCollectionViewCell.swift | 6 ++++++ .../FoldableCollectionViewCell.swift | 10 +++++++++- .../ImageCollectionViewCell.swift | 11 ++++++++++- .../SizableCollectionViewCell.swift | 10 +++++++++- .../TitleCollectionListCell.swift | 4 +--- .../TitleCollectionViewCell.swift | 17 ++++++++++++----- .../Footer/TitleIconCollectionFooterView.swift | 7 ++++++- .../HeaderCollectionListView.swift | 3 +-- .../TitleCollectionReusableView.swift | 7 ++++++- .../ImageCollectionViewCell.swift | 11 ++++++++++- ...ImageDefaultBehavoirCollectionViewCell.swift | 11 ++++++++++- .../Headers/TitleCollectionReusableView.swift | 7 ++++++- 13 files changed, 96 insertions(+), 19 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/DynamicHeightViewCell/Subviews/RectangleColorCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/DynamicHeightViewCell/Subviews/RectangleColorCollectionViewCell.swift index 0f882a602..33a4e2f06 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/DynamicHeightViewCell/Subviews/RectangleColorCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/DynamicHeightViewCell/Subviews/RectangleColorCollectionViewCell.swift @@ -8,16 +8,25 @@ import UIKit import ReactiveDataDisplayManager -final class RectangleColorCollectionViewCell: UICollectionViewCell, ConfigurableItem { +final class RectangleColorCollectionViewCell: UICollectionViewCell, ConfigurableItem, AccessibilityItem { // MARK: - @IBOutlets @IBOutlet private weak var colorView: UIView! + // MARK: - AccessibilityItem + + var labelStrategy: AccessibilityStringStrategy = .ignored + // MARK: - ConfigurableItem func configure(with model: UIColor) { colorView.backgroundColor = model + if #available(iOS 14.0, *) { + labelStrategy = .just(model.accessibilityName) + } else { + labelStrategy = .just("some color") + } colorView.layer.cornerRadius = 20 } diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FittingCompressedSizeCollectionViewCell/FittingCompressedSizeCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FittingCompressedSizeCollectionViewCell/FittingCompressedSizeCollectionViewCell.swift index 904b687e4..3fc55fe54 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FittingCompressedSizeCollectionViewCell/FittingCompressedSizeCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FittingCompressedSizeCollectionViewCell/FittingCompressedSizeCollectionViewCell.swift @@ -50,6 +50,12 @@ extension FittingCompressedSizeCollectionViewCell: ConfigurableItem { } +extension FittingCompressedSizeCollectionViewCell: AccessibilityItem { + + var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + +} + // MARK: - Configuration private extension FittingCompressedSizeCollectionViewCell { diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FoldableCollectionViewCell/FoldableCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FoldableCollectionViewCell/FoldableCollectionViewCell.swift index f5fc46bd0..716eecaaa 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FoldableCollectionViewCell/FoldableCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FoldableCollectionViewCell/FoldableCollectionViewCell.swift @@ -46,7 +46,7 @@ final class FoldableCollectionViewCell: UICollectionViewCell, FoldableStateHolde } -// MARK: - Configurable +// MARK: - ConfigurableItem extension FoldableCollectionViewCell: ConfigurableItem { @@ -56,6 +56,14 @@ extension FoldableCollectionViewCell: ConfigurableItem { } +// MARK: - AccessibilityItem + +extension FoldableCollectionViewCell: AccessibilityItem { + + var labelStrategy: AccessibilityStringStrategy { .just("Expandable item") } + +} + // MARK: - Configuration private extension FoldableCollectionViewCell { diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/ImageCollectionViewCell/ImageCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/ImageCollectionViewCell/ImageCollectionViewCell.swift index 750c42ee5..06eb7b956 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/ImageCollectionViewCell/ImageCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/ImageCollectionViewCell/ImageCollectionViewCell.swift @@ -44,7 +44,7 @@ final class ImageCollectionViewCell: UICollectionViewCell { } -// MARK: - Configurable +// MARK: - ConfigurableItem extension ImageCollectionViewCell: ConfigurableItem { @@ -54,6 +54,15 @@ extension ImageCollectionViewCell: ConfigurableItem { } +// MARK: - Configurable + +extension ImageCollectionViewCell: AccessibilityItem { + + var labelStrategy: AccessibilityStringStrategy { .just("some image") } + var traitsStrategy: AccessibilityTraitsStrategy { .just(.image) } + +} + // MARK: - Configuration private extension ImageCollectionViewCell { diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.swift index 756c456b5..aa16e97bd 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.swift @@ -37,7 +37,7 @@ final class SizableCollectionViewCell: UICollectionViewCell { } -// MARK: - Configurable +// MARK: - ConfigurableItem extension SizableCollectionViewCell: ConfigurableItem { @@ -47,6 +47,14 @@ extension SizableCollectionViewCell: ConfigurableItem { } +// MARK: - AccessibilityItem + +extension SizableCollectionViewCell: AccessibilityItem { + + var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + +} + // MARK: - Configuration private extension SizableCollectionViewCell { diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionListCell/TitleCollectionListCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionListCell/TitleCollectionListCell.swift index b45ecd9ea..97a3a7d0e 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionListCell/TitleCollectionListCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionListCell/TitleCollectionListCell.swift @@ -17,15 +17,13 @@ class TitleCollectionListCell: UICollectionViewCell, ConfigurableItem, Accessibi // MARK: - AccessibilityItem - var labelStrategy: AccessibilityStrategy { .from(titleLabel) } - var traitsStrategy: AccessibilityTraitsStrategy { .from(titleLabel) } + var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } // MARK: - Configurable func configure(with title: String) { titleLabel.text = title backgroundColor = .lightGray - accessibilityLabel = title } } diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift index f905465bb..9f406ca7e 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift @@ -28,34 +28,41 @@ extension TitleCollectionViewCell: ConfigurableItem { func configure(with title: String) { titleLabel.text = title - accessibilityLabel = title } } +// MARK: - AccessibilityItem + +extension TitleCollectionViewCell: AccessibilityItem { + + var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + +} + // MARK: - HighlightableItem extension TitleCollectionViewCell: HighlightableItem { func applyUnhighlightedStyle() { contentView.backgroundColor = .gray - accessibilityLabel = "Normal" + accessibilityValue = "Normal" } func applyHighlightedStyle() { contentView.backgroundColor = .white.withAlphaComponent(0.5) - accessibilityLabel = "Highlighted" + accessibilityValue = "Highlighted" } func applySelectedStyle() { contentView.layer.borderColor = UIColor.blue.cgColor contentView.layer.borderWidth = 1 - accessibilityLabel = "Selected" + accessibilityValue = "Selected" } func applyDeselectedStyle() { contentView.layer.borderWidth = .zero - accessibilityLabel = "Normal" + accessibilityValue = "Normal" } } diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Footer/TitleIconCollectionFooterView.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Footer/TitleIconCollectionFooterView.swift index bb82934ac..d2d7d72bb 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Footer/TitleIconCollectionFooterView.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Footer/TitleIconCollectionFooterView.swift @@ -6,14 +6,19 @@ // import UIKit +import ReactiveDataDisplayManager -class TitleIconCollectionFooterView: UICollectionReusableView { +class TitleIconCollectionFooterView: UICollectionReusableView, AccessibilityItem { // MARK: - IBOutlets @IBOutlet private weak var iconImageView: UIImageView! @IBOutlet private weak var titleLabel: UILabel! + // MARK: - AccessibilityItem + + var labelStrategy: AccessibilityStringStrategy { .joined([.just("some image"), .from(object: titleLabel)], separator: " with ") } + // MARK: - Internal methods func fill(title: String) { diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/HeaderCollectionListView/HeaderCollectionListView.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/HeaderCollectionListView/HeaderCollectionListView.swift index c8cbdda3b..38b292ab7 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/HeaderCollectionListView/HeaderCollectionListView.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/HeaderCollectionListView/HeaderCollectionListView.swift @@ -17,8 +17,7 @@ final class HeaderCollectionListView: UICollectionReusableView, AccessibilityIte // MARK: - AccessibilityItem - var labelStrategy: AccessibilityStrategy { .from(titleLabel) } - var traitsStrategy: AccessibilityTraitsStrategy { .from(titleLabel) } + var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } // MARK: - Internal Methods diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/TitleCollectionReusableView/TitleCollectionReusableView.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/TitleCollectionReusableView/TitleCollectionReusableView.swift index 704a7ab6e..3dc010394 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/TitleCollectionReusableView/TitleCollectionReusableView.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/TitleCollectionReusableView/TitleCollectionReusableView.swift @@ -7,13 +7,18 @@ // import UIKit +import ReactiveDataDisplayManager -final class TitleCollectionReusableView: UICollectionReusableView { +final class TitleCollectionReusableView: UICollectionReusableView, AccessibilityItem { // MARK: - IBOutlets @IBOutlet private weak var titleLabel: UILabel! + // MARK: - AccessibilityItem + + var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + // MARK: - Internal methods func fill(title: String) { diff --git a/Example/ReactiveDataDisplayManagerExampleTv/Views/Cells/ImageCollectionViewCell/ImageCollectionViewCell.swift b/Example/ReactiveDataDisplayManagerExampleTv/Views/Cells/ImageCollectionViewCell/ImageCollectionViewCell.swift index 4e94b8101..903b79e9c 100644 --- a/Example/ReactiveDataDisplayManagerExampleTv/Views/Cells/ImageCollectionViewCell/ImageCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManagerExampleTv/Views/Cells/ImageCollectionViewCell/ImageCollectionViewCell.swift @@ -44,7 +44,7 @@ final class ImageCollectionViewCell: UICollectionViewCell { } } -// MARK: - Configurable +// MARK: - ConfigurableItem extension ImageCollectionViewCell: ConfigurableItem { @@ -54,6 +54,15 @@ extension ImageCollectionViewCell: ConfigurableItem { } +// MARK: - AccessibilityItem + +extension ImageCollectionViewCell: AccessibilityItem { + + var labelStrategy: AccessibilityStringStrategy { .just("some image") } + var traitsStrategy: AccessibilityTraitsStrategy { .just(.image) } + +} + // MARK: - Configuration private extension ImageCollectionViewCell { diff --git a/Example/ReactiveDataDisplayManagerExampleTv/Views/Cells/ImageDefaultBehavoirCell/ImageDefaultBehavoirCollectionViewCell.swift b/Example/ReactiveDataDisplayManagerExampleTv/Views/Cells/ImageDefaultBehavoirCell/ImageDefaultBehavoirCollectionViewCell.swift index 47d878657..76f351384 100644 --- a/Example/ReactiveDataDisplayManagerExampleTv/Views/Cells/ImageDefaultBehavoirCell/ImageDefaultBehavoirCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManagerExampleTv/Views/Cells/ImageDefaultBehavoirCell/ImageDefaultBehavoirCollectionViewCell.swift @@ -30,7 +30,7 @@ final class ImageDefaultBehavoirCollectionViewCell: UICollectionViewCell { } -// MARK: - Configurable +// MARK: - ConfigurableItem extension ImageDefaultBehavoirCollectionViewCell: ConfigurableItem { @@ -40,6 +40,15 @@ extension ImageDefaultBehavoirCollectionViewCell: ConfigurableItem { } +// MARK: - AccessibilityItem + +extension ImageDefaultBehavoirCollectionViewCell: AccessibilityItem { + + var labelStrategy: AccessibilityStringStrategy { .just("some image") } + var traitsStrategy: AccessibilityTraitsStrategy { .just(.image) } + +} + // MARK: - Configuration private extension ImageDefaultBehavoirCollectionViewCell { diff --git a/Example/ReactiveDataDisplayManagerExampleTv/Views/Headers/TitleCollectionReusableView.swift b/Example/ReactiveDataDisplayManagerExampleTv/Views/Headers/TitleCollectionReusableView.swift index 4f512b2e2..983246906 100644 --- a/Example/ReactiveDataDisplayManagerExampleTv/Views/Headers/TitleCollectionReusableView.swift +++ b/Example/ReactiveDataDisplayManagerExampleTv/Views/Headers/TitleCollectionReusableView.swift @@ -6,13 +6,18 @@ // import UIKit +import ReactiveDataDisplayManager -final class TitleCollectionReusableView: UICollectionReusableView { +final class TitleCollectionReusableView: UICollectionReusableView, AccessibilityItem { // MARK: - IBOutlets @IBOutlet private weak var titleLabel: UILabel! + // MARK: - AccessibilityItem + + var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + // MARK: - Internal methods func fill(title: String) { From 102d52c8e5542df116b01b5957fec25c4e5025a1 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Tue, 30 May 2023 19:31:56 +0300 Subject: [PATCH 020/126] renamed default modifier, added custom actions support --- .../Plugins/Generators/AccessibilityItem.swift | 12 +++++++++--- Source/Utils/AccessibilityModifier.swift | 11 ++++++++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Source/Protocols/Plugins/Generators/AccessibilityItem.swift b/Source/Protocols/Plugins/Generators/AccessibilityItem.swift index a4e91027a..7e49de0ca 100644 --- a/Source/Protocols/Plugins/Generators/AccessibilityItem.swift +++ b/Source/Protocols/Plugins/Generators/AccessibilityItem.swift @@ -109,8 +109,12 @@ public extension AccessibilityStrategyProvider { } } -/// Protocol for cells -public protocol AccessibilityItem: UIView, AccessibilityStrategyProvider { +public protocol AccessibilityActionsProvider { + func accessibilityActions() -> [UIAccessibilityCustomAction] +} + +/// Protocol for cells to adopt accesibility +public protocol AccessibilityItem: UIView, AccessibilityStrategyProvider & AccessibilityActionsProvider { typealias AccessibilityModifierType = AccessibilityModifier.Type /// Type of modifier that will be used to apply strategies to view. Default it's internal implementation @@ -130,10 +134,12 @@ public protocol AccessibilityItem: UIView, AccessibilityStrategyProvider { } public extension AccessibilityItem { - var modifierType: AccessibilityModifierType { DefaultAccessibilityModifier.self } + var modifierType: AccessibilityModifierType { BaseAccessibilityModifier.self } func accessibilityStrategyConflictResolver(itemStrategy: AccessibilityStringStrategy, generatorStrategy: AccessibilityStringStrategy) -> String? { return [generatorStrategy, itemStrategy].compactMap(\.value).joined(separator: " ") } + + func accessibilityActions() -> [UIAccessibilityCustomAction] { [] } } diff --git a/Source/Utils/AccessibilityModifier.swift b/Source/Utils/AccessibilityModifier.swift index 2aacf6f9b..fdb90b862 100644 --- a/Source/Utils/AccessibilityModifier.swift +++ b/Source/Utils/AccessibilityModifier.swift @@ -16,7 +16,8 @@ public protocol AccessibilityModifier { static func modify(item: AccessibilityItem, generator: AccessibilityStrategyProvider) } -public enum DefaultAccessibilityModifier: AccessibilityModifier { +/// Base modifier which handle only `AccessibilityItem` properties +public enum BaseAccessibilityModifier: AccessibilityModifier { static public func modify(item: AccessibilityItem) { guard !item.isAccessibilityIgnored else { return @@ -34,6 +35,8 @@ public enum DefaultAccessibilityModifier: AccessibilityModifier { if !item.traitsStrategy.isIgnored, let traits = item.traitsStrategy.value { item.accessibilityTraits = traits } + + item.accessibilityCustomActions = item.accessibilityActions() } static public func modify(item: AccessibilityItem, generator: AccessibilityStrategyProvider) { @@ -58,5 +61,11 @@ public enum DefaultAccessibilityModifier: AccessibilityModifier { .reduce(UIAccessibilityTraits(), { $0.union($1) }) item.accessibilityTraits = traits } + + item.accessibilityCustomActions = item.accessibilityActions() + + if let actionsProvider = generator as? AccessibilityActionsProvider { + item.accessibilityCustomActions?.append(contentsOf: actionsProvider.accessibilityActions()) + } } } From cde4c3f445f239d0eec71804db60a22c377d88dc Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Tue, 30 May 2023 20:17:47 +0300 Subject: [PATCH 021/126] removed separator --- .../Views/Footer/TitleIconCollectionFooterView.swift | 2 +- .../Protocols/Plugins/Generators/AccessibilityItem.swift | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Footer/TitleIconCollectionFooterView.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Footer/TitleIconCollectionFooterView.swift index d2d7d72bb..1a24769b8 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Footer/TitleIconCollectionFooterView.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Footer/TitleIconCollectionFooterView.swift @@ -17,7 +17,7 @@ class TitleIconCollectionFooterView: UICollectionReusableView, AccessibilityItem // MARK: - AccessibilityItem - var labelStrategy: AccessibilityStringStrategy { .joined([.just("some image"), .from(object: titleLabel)], separator: " with ") } + var labelStrategy: AccessibilityStringStrategy { .joined([.just("some image with title: "), .from(object: titleLabel)]) } // MARK: - Internal methods diff --git a/Source/Protocols/Plugins/Generators/AccessibilityItem.swift b/Source/Protocols/Plugins/Generators/AccessibilityItem.swift index 7e49de0ca..b15fa58ee 100644 --- a/Source/Protocols/Plugins/Generators/AccessibilityItem.swift +++ b/Source/Protocols/Plugins/Generators/AccessibilityItem.swift @@ -20,8 +20,8 @@ public enum AccessibilityStringStrategy { /// - parameter keyPath: specified keypath from value would be taken case from(object: NSObject, keyPath: KeyPath = \.accessibilityLabel) - /// a combination of strategies, joined by provided separator - indirect case joined([AccessibilityStringStrategy], separator: String) + /// a combination of strategies + indirect case joined([AccessibilityStringStrategy]) var isIgnored: Bool { if case .ignored = self { @@ -39,8 +39,8 @@ public enum AccessibilityStringStrategy { return string case .from(let object, let keyPath): return object[keyPath: keyPath] - case .joined(let strategies, let separator): - return strategies.compactMap(\.value).joined(separator: separator) + case .joined(let strategies): + return strategies.compactMap(\.value).joined() } } } From 561931af346bc16c6498802df134cb1cbaef8c2f Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Tue, 30 May 2023 20:18:21 +0300 Subject: [PATCH 022/126] expandable table cell accessibility --- .../ExpandableTableCell.swift | 36 ++++++++++++++++++- .../ExpandableTableCell.xib | 9 ++--- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.swift index 2885aea15..f9d44c4a6 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.swift @@ -13,6 +13,7 @@ class ExpandableTableCell: UITableViewCell, ExpandableItem { // MARK: - IBOutlets @IBOutlet private weak var button: UIButton! + @IBOutlet private weak var switcher: UISwitch! @IBOutlet private weak var buttonHeightConstraint: NSLayoutConstraint! // MARK: - Private Properties @@ -24,7 +25,7 @@ class ExpandableTableCell: UITableViewCell, ExpandableItem { public var onHeightChanged: BaseEvent = .init() public var animatedExpandable = true - // MARK: - @IBActions + // MARK: - Actions @IBAction private func buttonTap(_ sender: UIButton) { buttonHeightConstraint.constant += isSmall ? 50 : -50 @@ -36,6 +37,39 @@ class ExpandableTableCell: UITableViewCell, ExpandableItem { animatedExpandable = sender.isOn } + override func accessibilityActivate() -> Bool { + buttonTap(button) + return true + } + + @objc + private func accessibilityActivateSwitch() -> Bool { + switcher.isOn.toggle() + return true + } + +} + +// MARK: - AccessibilityItem + +extension ExpandableTableCell: AccessibilityItem { + + var labelStrategy: AccessibilityStringStrategy { .from(object: button) } + var valueStrategy: AccessibilityStringStrategy { + .joined([ + .just(isSmall ? "collapsed" : "expanded"), + .just(", is animated: "), + .from(object: switcher, keyPath: \.accessibilityValue) + ]) + } + + var traitsStrategy: AccessibilityTraitsStrategy { .merge([button]) } + + func accessibilityActions() -> [UIAccessibilityCustomAction] { + let switchAction = UIAccessibilityCustomAction(name: "Toggle animated", target: self, selector: #selector(accessibilityActivateSwitch)) + return [switchAction] + } + } // MARK: - ConfigurableItem diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.xib b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.xib index f6164dcd8..2c570dd6d 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.xib +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.xib @@ -1,9 +1,9 @@ - + - + @@ -32,8 +32,8 @@ - - + + @@ -52,6 +52,7 @@ + From 2e4b6610f181f024d0e57473fdfeb12c37c585cf Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Wed, 31 May 2023 13:17:08 +0300 Subject: [PATCH 023/126] update structure --- .../AccessibilityItem+Invalidation.swift | 8 ++ .../AccessibilityItem+Providers.swift | 8 ++ .../AccessibilityItem+Strategies.swift | 85 +++++++++++++++++++ .../AccessibilityItem.swift | 33 +++++++ .../AccessibilityModifier.swift | 0 5 files changed, 134 insertions(+) create mode 100644 Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift create mode 100644 Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Providers.swift create mode 100644 Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Strategies.swift rename Source/Protocols/Plugins/Generators/{ => Accessibility}/AccessibilityItem.swift (82%) rename Source/{Utils => Protocols/Plugins/Generators/Accessibility}/AccessibilityModifier.swift (100%) diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift new file mode 100644 index 000000000..6d3eca163 --- /dev/null +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift @@ -0,0 +1,8 @@ +// +// AccessibilityItem+Invalidation.swift +// ReactiveDataDisplayManager +// +// Created by korshunov on 31.05.2023. +// + +import Foundation diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Providers.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Providers.swift new file mode 100644 index 000000000..9dec1bad0 --- /dev/null +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Providers.swift @@ -0,0 +1,8 @@ +// +// AccessibilityItem+Providers.swift +// ReactiveDataDisplayManager +// +// Created by korshunov on 31.05.2023. +// + +import Foundation diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Strategies.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Strategies.swift new file mode 100644 index 000000000..4aa5c8922 --- /dev/null +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Strategies.swift @@ -0,0 +1,85 @@ +// +// AccessibilityStrategies.swift +// ReactiveDataDisplayManager +// +// Created by korshunov on 31.05.2023. +// + +import UIKit + +/// Default accessibility strategy for string value +public enum AccessibilityStringStrategy { + + /// value will not be change by this strategy + case ignored + + /// simple string value, can be nil + case just(String?) + + /// a reference value from object. By default it's `accessibilityLabel` + /// - parameter keyPath: specified keypath from value would be taken + case from(object: NSObject, keyPath: KeyPath = \.accessibilityLabel) + + /// a combination of strategies + indirect case joined([AccessibilityStringStrategy]) + + var isIgnored: Bool { + if case .ignored = self { + return true + } else { + return false + } + } + + var value: String? { + switch self { + case .ignored: + return nil + case .just(let string): + return string + case .from(let object, let keyPath): + return object[keyPath: keyPath] + case .joined(let strategies): + return strategies.compactMap(\.value).joined() + } + } +} + +/// Accessibility strategy for `UIAccessibilityTraits` parameter +public enum AccessibilityTraitsStrategy { + + /// value will not be change by this strategy + case ignored + + /// simple accessibility traits + case just(UIAccessibilityTraits) + + /// a reference traits from another object + case from(object: NSObject) + + /// a reference traits merged from another objects + case merge([NSObject]) + + var isIgnored: Bool { + if case .ignored = self { + return true + } else { + return false + } + } + + /// accessibility traits of current strategy + /// - Returns: nil if `isIgnored`, otherwise `UIAccessibilityTraits` + var value: UIAccessibilityTraits? { + switch self { + case .ignored: + return nil + case .just(let traits): + return traits + case .from(let object): + return object.accessibilityTraits + case .merge(let objects): + return objects.map(\.accessibilityTraits).reduce(UIAccessibilityTraits(), { $0.union($1) }) + } + } +} diff --git a/Source/Protocols/Plugins/Generators/AccessibilityItem.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift similarity index 82% rename from Source/Protocols/Plugins/Generators/AccessibilityItem.swift rename to Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift index b15fa58ee..f5b9f287b 100644 --- a/Source/Protocols/Plugins/Generators/AccessibilityItem.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift @@ -143,3 +143,36 @@ public extension AccessibilityItem { func accessibilityActions() -> [UIAccessibilityCustomAction] { [] } } + +public enum AccessibilityItemKind { + case header(Int), cell(IndexPath), footer(Int) +} + +public struct AccessibilityItemInvalidator { + let accessibilityItemKind: AccessibilityItemKind + weak var item: AccessibilityItem? + weak var accessibilityDelegate: AccessibilityItemDelegate? + + public func invalidateAccessibility() { + guard let item else { return } + accessibilityDelegate?.didInvalidateAccessibility(for: item, of: accessibilityItemKind) + } +} + +public protocol AccessibilityInvalidatable: AccessibilityItem { + var accessibilityInvalidator: AccessibilityItemInvalidator? { get set } +} + +extension AccessibilityInvalidatable { + func setInvalidator(kind: AccessibilityItemKind, delegate: AccessibilityItemDelegate) { + accessibilityInvalidator = AccessibilityItemInvalidator(accessibilityItemKind: kind, item: self, accessibilityDelegate: delegate) + } + + func removeInvalidator() { + accessibilityInvalidator = nil + } +} + +public protocol AccessibilityItemDelegate: AnyObject { + func didInvalidateAccessibility(for item: AccessibilityItem, of kind: AccessibilityItemKind) +} diff --git a/Source/Utils/AccessibilityModifier.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityModifier.swift similarity index 100% rename from Source/Utils/AccessibilityModifier.swift rename to Source/Protocols/Plugins/Generators/Accessibility/AccessibilityModifier.swift From 728d61b563a1af2879794c78954ce8cd3b30f75e Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Wed, 31 May 2023 13:18:12 +0300 Subject: [PATCH 024/126] added accessibility invalidation --- .../AccessibilityItem+Invalidation.swift | 33 +++++ .../AccessibilityItem+Providers.swift | 31 +++- .../Accessibility/AccessibilityItem.swift | 139 +----------------- Source/Table/Delegate/BaseTableDelegate.swift | 24 +++ Source/Table/Events/TableEvent.swift | 3 + .../TableAccessibilityPlugin.swift | 6 +- 6 files changed, 94 insertions(+), 142 deletions(-) diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift index 6d3eca163..f9fe2fccc 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift @@ -6,3 +6,36 @@ // import Foundation + +public enum AccessibilityItemKind { + case header(Int), cell(IndexPath), footer(Int) +} + +public struct AccessibilityItemInvalidator { + let accessibilityItemKind: AccessibilityItemKind + weak var item: AccessibilityItem? + weak var accessibilityDelegate: AccessibilityItemDelegate? + + public func invalidateAccessibility() { + guard let item else { return } + accessibilityDelegate?.didInvalidateAccessibility(for: item, of: accessibilityItemKind) + } +} + +public protocol AccessibilityInvalidatable: AccessibilityItem { + var accessibilityInvalidator: AccessibilityItemInvalidator? { get set } +} + +extension AccessibilityInvalidatable { + func setInvalidator(kind: AccessibilityItemKind, delegate: AccessibilityItemDelegate) { + accessibilityInvalidator = AccessibilityItemInvalidator(accessibilityItemKind: kind, item: self, accessibilityDelegate: delegate) + } + + func removeInvalidator() { + accessibilityInvalidator = nil + } +} + +public protocol AccessibilityItemDelegate: AnyObject { + func didInvalidateAccessibility(for item: AccessibilityItem, of kind: AccessibilityItemKind) +} diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Providers.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Providers.swift index 9dec1bad0..6dda4e233 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Providers.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Providers.swift @@ -5,4 +5,33 @@ // Created by korshunov on 31.05.2023. // -import Foundation +import UIKit + +/// Accessibility strategies provider protocol for generators +public protocol AccessibilityStrategyProvider { + + /// strategy for `accessibilityLabel`. Default: `.ignored` + var labelStrategy: AccessibilityStringStrategy { get } + + /// strategy for `accessibilityValue`. Default: `.ignored` + var valueStrategy: AccessibilityStringStrategy { get } + + /// strategy for `accessibilityTraits`. Default: `.ignored` + var traitsStrategy: AccessibilityTraitsStrategy { get } + + /// Idicates that `AccessibilityItem` should become an accessibility element. Equals `true` if all strategies is in state `.ignored` + var isAccessibilityIgnored: Bool { get } +} + +public extension AccessibilityStrategyProvider { + var labelStrategy: AccessibilityStringStrategy { .ignored } + var valueStrategy: AccessibilityStringStrategy { .ignored } + var traitsStrategy: AccessibilityTraitsStrategy { .ignored } + var isAccessibilityIgnored: Bool { + return [labelStrategy.isIgnored, valueStrategy.isIgnored, traitsStrategy.isIgnored].allSatisfy { $0 } + } +} + +public protocol AccessibilityActionsProvider { + func accessibilityActions() -> [UIAccessibilityCustomAction] +} diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift index f5b9f287b..0190cc546 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift @@ -7,114 +7,9 @@ import UIKit -/// Default accessibility strategy for string value -public enum AccessibilityStringStrategy { - - /// value will not be change by this strategy - case ignored - - /// simple string value, can be nil - case just(String?) - - /// a reference value from object. By default it's `accessibilityLabel` - /// - parameter keyPath: specified keypath from value would be taken - case from(object: NSObject, keyPath: KeyPath = \.accessibilityLabel) - - /// a combination of strategies - indirect case joined([AccessibilityStringStrategy]) - - var isIgnored: Bool { - if case .ignored = self { - return true - } else { - return false - } - } - - var value: String? { - switch self { - case .ignored: - return nil - case .just(let string): - return string - case .from(let object, let keyPath): - return object[keyPath: keyPath] - case .joined(let strategies): - return strategies.compactMap(\.value).joined() - } - } -} - -/// Accessibility strategy for `UIAccessibilityTraits` parameter -public enum AccessibilityTraitsStrategy { - - /// value will not be change by this strategy - case ignored - - /// simple accessibility traits - case just(UIAccessibilityTraits) - - /// a reference traits from another object - case from(object: NSObject) - - /// a reference traits merged from another objects - case merge([NSObject]) - - var isIgnored: Bool { - if case .ignored = self { - return true - } else { - return false - } - } - - /// accessibility traits of current strategy - /// - Returns: nil if `isIgnored`, otherwise `UIAccessibilityTraits` - var value: UIAccessibilityTraits? { - switch self { - case .ignored: - return nil - case .just(let traits): - return traits - case .from(let object): - return object.accessibilityTraits - case .merge(let objects): - return objects.map(\.accessibilityTraits).reduce(UIAccessibilityTraits(), { $0.union($1) }) - } - } -} - -/// Accessibility strategies provider protocol for generators -public protocol AccessibilityStrategyProvider { - - /// strategy for `accessibilityLabel`. Default: `.ignored` - var labelStrategy: AccessibilityStringStrategy { get } - - /// strategy for `accessibilityValue`. Default: `.ignored` - var valueStrategy: AccessibilityStringStrategy { get } - - /// strategy for `accessibilityTraits`. Default: `.ignored` - var traitsStrategy: AccessibilityTraitsStrategy { get } - - /// Idicates that `AccessibilityItem` should become an accessibility element. Equals `true` if all strategies is in state `.ignored` - var isAccessibilityIgnored: Bool { get } -} - -public extension AccessibilityStrategyProvider { - var labelStrategy: AccessibilityStringStrategy { .ignored } - var valueStrategy: AccessibilityStringStrategy { .ignored } - var traitsStrategy: AccessibilityTraitsStrategy { .ignored } - var isAccessibilityIgnored: Bool { - return [labelStrategy.isIgnored, valueStrategy.isIgnored, traitsStrategy.isIgnored].allSatisfy { $0 } - } -} - -public protocol AccessibilityActionsProvider { - func accessibilityActions() -> [UIAccessibilityCustomAction] -} - /// Protocol for cells to adopt accesibility public protocol AccessibilityItem: UIView, AccessibilityStrategyProvider & AccessibilityActionsProvider { + typealias AccessibilityModifierType = AccessibilityModifier.Type /// Type of modifier that will be used to apply strategies to view. Default it's internal implementation @@ -144,35 +39,3 @@ public extension AccessibilityItem { func accessibilityActions() -> [UIAccessibilityCustomAction] { [] } } -public enum AccessibilityItemKind { - case header(Int), cell(IndexPath), footer(Int) -} - -public struct AccessibilityItemInvalidator { - let accessibilityItemKind: AccessibilityItemKind - weak var item: AccessibilityItem? - weak var accessibilityDelegate: AccessibilityItemDelegate? - - public func invalidateAccessibility() { - guard let item else { return } - accessibilityDelegate?.didInvalidateAccessibility(for: item, of: accessibilityItemKind) - } -} - -public protocol AccessibilityInvalidatable: AccessibilityItem { - var accessibilityInvalidator: AccessibilityItemInvalidator? { get set } -} - -extension AccessibilityInvalidatable { - func setInvalidator(kind: AccessibilityItemKind, delegate: AccessibilityItemDelegate) { - accessibilityInvalidator = AccessibilityItemInvalidator(accessibilityItemKind: kind, item: self, accessibilityDelegate: delegate) - } - - func removeInvalidator() { - accessibilityInvalidator = nil - } -} - -public protocol AccessibilityItemDelegate: AnyObject { - func didInvalidateAccessibility(for item: AccessibilityItem, of kind: AccessibilityItemKind) -} diff --git a/Source/Table/Delegate/BaseTableDelegate.swift b/Source/Table/Delegate/BaseTableDelegate.swift index a6967cdbd..9301cfa1a 100644 --- a/Source/Table/Delegate/BaseTableDelegate.swift +++ b/Source/Table/Delegate/BaseTableDelegate.swift @@ -93,26 +93,32 @@ extension BaseTableDelegate { open func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { tablePlugins.process(event: .willDisplayCell(indexPath, cell), with: manager) + (cell as? AccessibilityInvalidatable)?.setInvalidator(kind: .cell(indexPath), delegate: self) } open func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) { tablePlugins.process(event: .willDisplayHeader(section, view), with: manager) + (view as? AccessibilityInvalidatable)?.setInvalidator(kind: .header(section), delegate: self) } open func tableView(_ tableView: UITableView, didEndDisplayingHeaderView view: UIView, forSection section: Int) { tablePlugins.process(event: .didEndDisplayHeader(section, view), with: manager) + (view as? AccessibilityInvalidatable)?.removeInvalidator() } open func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) { tablePlugins.process(event: .didEndDisplayCell(indexPath, cell), with: manager) + (cell as? AccessibilityInvalidatable)?.removeInvalidator() } open func tableView(_ tableView: UITableView, willDisplayFooterView view: UIView, forSection section: Int) { tablePlugins.process(event: .willDisplayFooter(section, view), with: manager) + (view as? AccessibilityInvalidatable)?.setInvalidator(kind: .footer(section), delegate: self) } open func tableView(_ tableView: UITableView, didEndDisplayingFooterView view: UIView, forSection section: Int) { tablePlugins.process(event: .didEndDisplayFooter(section, view), with: manager) + (view as? AccessibilityInvalidatable)?.removeInvalidator() } open func tableView(_ tableView: UITableView, canFocusRowAt indexPath: IndexPath) -> Bool { @@ -258,6 +264,24 @@ extension BaseTableDelegate: TableDragAndDropDelegate { } #endif +// MARK: AccessibilityItemDelegate + +extension BaseTableDelegate: AccessibilityItemDelegate { + + public func didInvalidateAccessibility(for item: AccessibilityItem, of kind: AccessibilityItemKind) { + switch kind { + case .header(let section): + tablePlugins.process(event: .invalidatedHeaderAccessibility(section, item), with: manager) + case .cell(let indexPath): + guard let cell = item as? UITableViewCell else { return } + tablePlugins.process(event: .invalidatedCellAccessibility(indexPath, cell), with: manager) + case .footer(let section): + tablePlugins.process(event: .invalidatedFooterAccessibility(section, item), with: manager) + } + } + +} + // MARK: UIScrollViewDelegate extension BaseTableDelegate { diff --git a/Source/Table/Events/TableEvent.swift b/Source/Table/Events/TableEvent.swift index a79995a66..1883c55c2 100644 --- a/Source/Table/Events/TableEvent.swift +++ b/Source/Table/Events/TableEvent.swift @@ -24,6 +24,9 @@ public enum TableEvent { case didEndDisplayHeader(Int, UIView) case willDisplayFooter(Int, UIView) case didEndDisplayFooter(Int, UIView) + case invalidatedCellAccessibility(IndexPath, UITableViewCell) + case invalidatedHeaderAccessibility(Int, UIView) + case invalidatedFooterAccessibility(Int, UIView) case didUpdateFocus(context: UITableViewFocusUpdateContext, coordinator: UIFocusAnimationCoordinator) case move(from: IndexPath, to: IndexPath) } diff --git a/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift b/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift index 33025a0c3..a1f47f23a 100644 --- a/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift +++ b/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift @@ -13,7 +13,7 @@ final class TableAccessibilityPlugin: BaseTablePlugin { override func process(event: TableEvent, with manager: BaseTableManager?) { switch event { - case .willDisplayCell(let indexPath, let cell): + case let .willDisplayCell(indexPath, cell), let .invalidatedCellAccessibility(indexPath, cell): guard let accessibilityItem = cell as? AccessibilityItem else { return } @@ -23,7 +23,7 @@ final class TableAccessibilityPlugin: BaseTablePlugin { accessibilityItem.modifierType.modify(item: accessibilityItem) } - case .willDisplayHeader(let section, let view): + case let .willDisplayHeader(section, view), let .invalidatedHeaderAccessibility(section, view): guard let accessibilityItem = view as? AccessibilityItem else { return } @@ -33,7 +33,7 @@ final class TableAccessibilityPlugin: BaseTablePlugin { accessibilityItem.modifierType.modify(item: accessibilityItem) } - case .willDisplayFooter(_, let view): + case let .willDisplayFooter(_, view), let .invalidatedFooterAccessibility(_, view): guard let accessibilityItem = view as? AccessibilityItem else { return } From c5134364c8ac3066e9dca5aef455794b4538e694 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Wed, 31 May 2023 14:05:04 +0300 Subject: [PATCH 025/126] added accessibility to expandable table cell --- .../ExpandableTableCell.swift | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.swift index f9d44c4a6..659c4c81f 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.swift @@ -8,7 +8,7 @@ import UIKit import ReactiveDataDisplayManager -class ExpandableTableCell: UITableViewCell, ExpandableItem { +class ExpandableTableCell: UITableViewCell, ExpandableItem, AccessibilityInvalidatable { // MARK: - IBOutlets @@ -18,12 +18,24 @@ class ExpandableTableCell: UITableViewCell, ExpandableItem { // MARK: - Private Properties - private var isSmall = true + private var isSmall = true { + didSet { + accessibilityInvalidator?.invalidateAccessibility() + } + } // MARK: - ExpandableItem Properties public var onHeightChanged: BaseEvent = .init() - public var animatedExpandable = true + public var animatedExpandable = true { + didSet { + accessibilityInvalidator?.invalidateAccessibility() + } + } + + // MARK: - AccessibilityInvalidatable + + var accessibilityInvalidator: AccessibilityItemInvalidator? // MARK: - Actions @@ -63,7 +75,7 @@ extension ExpandableTableCell: AccessibilityItem { ]) } - var traitsStrategy: AccessibilityTraitsStrategy { .merge([button]) } + var traitsStrategy: AccessibilityTraitsStrategy { .from(object: button) } func accessibilityActions() -> [UIAccessibilityCustomAction] { let switchAction = UIAccessibilityCustomAction(name: "Toggle animated", target: self, selector: #selector(accessibilityActivateSwitch)) From 192ff530b5b0c72d978fd56706bcddd4cc063a1e Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Wed, 31 May 2023 14:05:18 +0300 Subject: [PATCH 026/126] added public modifiers --- .../Accessibility/AccessibilityItem+Strategies.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Strategies.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Strategies.swift index 4aa5c8922..d8bfc762a 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Strategies.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Strategies.swift @@ -23,7 +23,7 @@ public enum AccessibilityStringStrategy { /// a combination of strategies indirect case joined([AccessibilityStringStrategy]) - var isIgnored: Bool { + public var isIgnored: Bool { if case .ignored = self { return true } else { @@ -31,7 +31,7 @@ public enum AccessibilityStringStrategy { } } - var value: String? { + public var value: String? { switch self { case .ignored: return nil @@ -60,7 +60,7 @@ public enum AccessibilityTraitsStrategy { /// a reference traits merged from another objects case merge([NSObject]) - var isIgnored: Bool { + public var isIgnored: Bool { if case .ignored = self { return true } else { @@ -70,7 +70,7 @@ public enum AccessibilityTraitsStrategy { /// accessibility traits of current strategy /// - Returns: nil if `isIgnored`, otherwise `UIAccessibilityTraits` - var value: UIAccessibilityTraits? { + public var value: UIAccessibilityTraits? { switch self { case .ignored: return nil From ea858b8e9c6e9d1c72158a4f574db44d5b0b4e90 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Wed, 31 May 2023 17:00:38 +0300 Subject: [PATCH 027/126] added accessibility invalidator --- .../Delegate/BaseCollectionDelegate.swift | 20 +++++ .../Delegate/CollectionDelegate.swift | 2 +- .../Collection/Events/CollectionEvent.swift | 3 + .../CollectionAccessibilityPlugin.swift | 72 ++++++++++++----- .../AccessibilityItem+Invalidation.swift | 2 +- Source/Table/Delegate/BaseTableDelegate.swift | 8 +- Source/Table/Delegate/TableDelegate.swift | 2 +- .../TableAccessibilityPlugin.swift | 81 ++++++++++++------- 8 files changed, 132 insertions(+), 58 deletions(-) diff --git a/Source/Collection/Delegate/BaseCollectionDelegate.swift b/Source/Collection/Delegate/BaseCollectionDelegate.swift index 6b314cf48..268fa64df 100644 --- a/Source/Collection/Delegate/BaseCollectionDelegate.swift +++ b/Source/Collection/Delegate/BaseCollectionDelegate.swift @@ -198,6 +198,26 @@ extension BaseCollectionDelegate { } #endif +// MARK: AccessibilityItemDelegate + +extension BaseCollectionDelegate { + + public func didInvalidateAccessibility(for item: AccessibilityItem, of kind: AccessibilityItemKind) { + switch kind { + case .header(let section): + guard let view = item as? UICollectionReusableView else { return } + collectionPlugins.process(event: .invalidatedHeaderAccessibility(section, view), with: manager) + case .cell(let indexPath): + guard let cell = item as? UICollectionViewCell else { return } + collectionPlugins.process(event: .invalidatedCellAccessibility(indexPath, cell), with: manager) + case .footer(let section): + guard let view = item as? UICollectionReusableView else { return } + collectionPlugins.process(event: .invalidatedFooterAccessibility(section, view), with: manager) + } + } + +} + // MARK: UIScrollViewDelegate extension BaseCollectionDelegate { diff --git a/Source/Collection/Delegate/CollectionDelegate.swift b/Source/Collection/Delegate/CollectionDelegate.swift index f2b19061d..08a4e39f2 100644 --- a/Source/Collection/Delegate/CollectionDelegate.swift +++ b/Source/Collection/Delegate/CollectionDelegate.swift @@ -8,7 +8,7 @@ import UIKit -public protocol CollectionDelegate: CollectionBuilderConfigurable, UICollectionViewDelegate { +public protocol CollectionDelegate: CollectionBuilderConfigurable, UICollectionViewDelegate, AccessibilityItemDelegate { var manager: BaseCollectionManager? { get set } var collectionPlugins: PluginCollection> { get set } var scrollPlugins: PluginCollection> { get set } diff --git a/Source/Collection/Events/CollectionEvent.swift b/Source/Collection/Events/CollectionEvent.swift index 19a0221dd..0c03dbf21 100644 --- a/Source/Collection/Events/CollectionEvent.swift +++ b/Source/Collection/Events/CollectionEvent.swift @@ -17,5 +17,8 @@ public enum CollectionEvent { case didEndDisplayCell(IndexPath, UICollectionViewCell) case willDisplaySupplementaryView(IndexPath, UICollectionReusableView, String) case didEndDisplayingSupplementaryView(IndexPath, UICollectionReusableView, String) + case invalidatedCellAccessibility(IndexPath, UICollectionViewCell) + case invalidatedHeaderAccessibility(Int, UICollectionReusableView) + case invalidatedFooterAccessibility(Int, UICollectionReusableView) case move(from: IndexPath, to: IndexPath) } diff --git a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift index d92843d3a..90d0d2f5a 100644 --- a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift +++ b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift @@ -11,42 +11,72 @@ final class CollectionAccessibilityPlugin: BaseCollectionPlugin override func process(event: CollectionEvent, with manager: BaseCollectionManager?) { switch event { - case .willDisplayCell(let indexPath, let cell): - guard let accessibilityItem = cell as? AccessibilityItem else { - return - } - if let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityStrategyProvider { - accessibilityItem.modifierType.modify(item: accessibilityItem, generator: generator) - } else { - accessibilityItem.modifierType.modify(item: accessibilityItem) - } + case let .willDisplayCell(indexPath, cell): + processCollectionCell(indexPath, cell, with: manager) + (cell as? AccessibilityInvalidatable)?.setInvalidator(kind: .cell(indexPath), delegate: manager?.delegate) - case .willDisplaySupplementaryView(let indexPath, let view, let kind): - guard let accessibilityItem = view as? AccessibilityItem else { - return - } + case let .invalidatedCellAccessibility(indexPath, cell): + processCollectionCell(indexPath, cell, with: manager) - var supplementaryGenerator: AccessibilityStrategyProvider? + case let .willDisplaySupplementaryView(indexPath, view, kind): switch kind { case UICollectionView.elementKindSectionHeader: - supplementaryGenerator = manager?.sections[indexPath.section] as? AccessibilityStrategyProvider + processCollectionHeader(indexPath.section, view, with: manager) + (view as? AccessibilityInvalidatable)?.setInvalidator(kind: .header(indexPath.section), delegate: manager?.delegate) case UICollectionView.elementKindSectionFooter: - supplementaryGenerator = manager?.footers[indexPath.section] as? AccessibilityStrategyProvider + processCollectionFooter(indexPath.section, view, with: manager) + (view as? AccessibilityInvalidatable)?.setInvalidator(kind: .footer(indexPath.section), delegate: manager?.delegate) default: break } - if let supplementaryGenerator { - accessibilityItem.modifierType.modify(item: accessibilityItem, generator: supplementaryGenerator) - } else { - accessibilityItem.modifierType.modify(item: accessibilityItem) - } + case let .invalidatedHeaderAccessibility(section, view): + processCollectionHeader(section, view, with: manager) + + case let .invalidatedFooterAccessibility(section, view): + processCollectionFooter(section, view, with: manager) + + case let .didEndDisplayCell(_, view as UIView), let .didEndDisplayingSupplementaryView(_, view as UIView, _): + (view as? AccessibilityInvalidatable)?.removeInvalidator() default: break } } + private func processCollectionCell(_ indexPath: IndexPath, _ cell: UICollectionViewCell, with manager: BaseCollectionManager?) { + guard let accessibilityItem = cell as? AccessibilityItem else { + return + } + if let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityStrategyProvider { + accessibilityItem.modifierType.modify(item: accessibilityItem, generator: generator) + } else { + accessibilityItem.modifierType.modify(item: accessibilityItem) + } + } + + private func processCollectionHeader(_ section: Int, _ view: UICollectionReusableView, with manager: BaseCollectionManager?) { + guard let accessibilityItem = view as? AccessibilityItem else { + return + } + if let header = manager?.sections[section] as? AccessibilityStrategyProvider { + accessibilityItem.modifierType.modify(item: accessibilityItem, generator: header) + } else { + accessibilityItem.modifierType.modify(item: accessibilityItem) + } + } + + private func processCollectionFooter(_ section: Int, _ view: UICollectionReusableView, with manager: BaseCollectionManager?) { + guard let accessibilityItem = view as? AccessibilityItem else { + return + } + if let footer = manager?.footers[section] as? AccessibilityStrategyProvider { + accessibilityItem.modifierType.modify(item: accessibilityItem, generator: footer) + } else { + accessibilityItem.modifierType.modify(item: accessibilityItem) + } + } + } extension BaseCollectionPlugin { diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift index f9fe2fccc..13d94a488 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift @@ -27,7 +27,7 @@ public protocol AccessibilityInvalidatable: AccessibilityItem { } extension AccessibilityInvalidatable { - func setInvalidator(kind: AccessibilityItemKind, delegate: AccessibilityItemDelegate) { + func setInvalidator(kind: AccessibilityItemKind, delegate: AccessibilityItemDelegate?) { accessibilityInvalidator = AccessibilityItemInvalidator(accessibilityItemKind: kind, item: self, accessibilityDelegate: delegate) } diff --git a/Source/Table/Delegate/BaseTableDelegate.swift b/Source/Table/Delegate/BaseTableDelegate.swift index 9301cfa1a..27978892e 100644 --- a/Source/Table/Delegate/BaseTableDelegate.swift +++ b/Source/Table/Delegate/BaseTableDelegate.swift @@ -93,32 +93,26 @@ extension BaseTableDelegate { open func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { tablePlugins.process(event: .willDisplayCell(indexPath, cell), with: manager) - (cell as? AccessibilityInvalidatable)?.setInvalidator(kind: .cell(indexPath), delegate: self) } open func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) { tablePlugins.process(event: .willDisplayHeader(section, view), with: manager) - (view as? AccessibilityInvalidatable)?.setInvalidator(kind: .header(section), delegate: self) } open func tableView(_ tableView: UITableView, didEndDisplayingHeaderView view: UIView, forSection section: Int) { tablePlugins.process(event: .didEndDisplayHeader(section, view), with: manager) - (view as? AccessibilityInvalidatable)?.removeInvalidator() } open func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) { tablePlugins.process(event: .didEndDisplayCell(indexPath, cell), with: manager) - (cell as? AccessibilityInvalidatable)?.removeInvalidator() } open func tableView(_ tableView: UITableView, willDisplayFooterView view: UIView, forSection section: Int) { tablePlugins.process(event: .willDisplayFooter(section, view), with: manager) - (view as? AccessibilityInvalidatable)?.setInvalidator(kind: .footer(section), delegate: self) } open func tableView(_ tableView: UITableView, didEndDisplayingFooterView view: UIView, forSection section: Int) { tablePlugins.process(event: .didEndDisplayFooter(section, view), with: manager) - (view as? AccessibilityInvalidatable)?.removeInvalidator() } open func tableView(_ tableView: UITableView, canFocusRowAt indexPath: IndexPath) -> Bool { @@ -266,7 +260,7 @@ extension BaseTableDelegate: TableDragAndDropDelegate { // MARK: AccessibilityItemDelegate -extension BaseTableDelegate: AccessibilityItemDelegate { +extension BaseTableDelegate { public func didInvalidateAccessibility(for item: AccessibilityItem, of kind: AccessibilityItemKind) { switch kind { diff --git a/Source/Table/Delegate/TableDelegate.swift b/Source/Table/Delegate/TableDelegate.swift index fc8f31d0f..9c606126c 100644 --- a/Source/Table/Delegate/TableDelegate.swift +++ b/Source/Table/Delegate/TableDelegate.swift @@ -8,7 +8,7 @@ import UIKit -public protocol TableDelegate: TableBuilderConfigurable, UITableViewDelegate { +public protocol TableDelegate: TableBuilderConfigurable, UITableViewDelegate, AccessibilityItemDelegate { var manager: BaseTableManager? { get set } var tablePlugins: PluginCollection> { get set } diff --git a/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift b/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift index a1f47f23a..2a2a02efa 100644 --- a/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift +++ b/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift @@ -13,39 +13,66 @@ final class TableAccessibilityPlugin: BaseTablePlugin { override func process(event: TableEvent, with manager: BaseTableManager?) { switch event { - case let .willDisplayCell(indexPath, cell), let .invalidatedCellAccessibility(indexPath, cell): - guard let accessibilityItem = cell as? AccessibilityItem else { - return - } - if let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityStrategyProvider { - accessibilityItem.modifierType.modify(item: accessibilityItem, generator: generator) - } else { - accessibilityItem.modifierType.modify(item: accessibilityItem) - } - - case let .willDisplayHeader(section, view), let .invalidatedHeaderAccessibility(section, view): - guard let accessibilityItem = view as? AccessibilityItem else { - return - } - if let headerGenerator = manager?.sections[section] as? AccessibilityStrategyProvider { - accessibilityItem.modifierType.modify(item: accessibilityItem, generator: headerGenerator) - } else { - accessibilityItem.modifierType.modify(item: accessibilityItem) - } - - case let .willDisplayFooter(_, view), let .invalidatedFooterAccessibility(_, view): - guard let accessibilityItem = view as? AccessibilityItem else { - return - } - // AccessibilityStrategyProvider for a footer generator is not supported yet - // TODO: SPT-1468 - accessibilityItem.modifierType.modify(item: accessibilityItem) + case let .willDisplayCell(indexPath, cell): + processTableCell(indexPath, cell, with: manager) + (cell as? AccessibilityInvalidatable)?.setInvalidator(kind: .cell(indexPath), delegate: manager?.delegate) + + case let .invalidatedCellAccessibility(indexPath, cell): + processTableCell(indexPath, cell, with: manager) + + case let .willDisplayHeader(section, view): + processTableHeader(section, view, with: manager) + (view as? AccessibilityInvalidatable)?.setInvalidator(kind: .header(section), delegate: manager?.delegate) + + case let .invalidatedHeaderAccessibility(section, view): + processTableHeader(section, view, with: manager) + + case let .willDisplayFooter(section, view): + processTableFooter(section, view, with: manager) + (view as? AccessibilityInvalidatable)?.setInvalidator(kind: .footer(section), delegate: manager?.delegate) + + case let .invalidatedFooterAccessibility(section, view): + processTableFooter(section, view, with: manager) + + case let .didEndDisplayFooter(_, view), let .didEndDisplayHeader(_, view), let .didEndDisplayCell(_, view as UIView): + (view as? AccessibilityInvalidatable)?.removeInvalidator() default: break } } + private func processTableCell(_ indexPath: IndexPath, _ cell: UITableViewCell, with manager: BaseTableManager?) { + guard let accessibilityItem = cell as? AccessibilityItem else { + return + } + if let generator = manager?.generators[indexPath.section][indexPath.row] as? AccessibilityStrategyProvider { + accessibilityItem.modifierType.modify(item: accessibilityItem, generator: generator) + } else { + accessibilityItem.modifierType.modify(item: accessibilityItem) + } + } + + private func processTableHeader(_ section: Int, _ view: UIView, with manager: BaseTableManager?) { + guard let accessibilityItem = view as? AccessibilityItem else { + return + } + if let header = manager?.sections[section] as? AccessibilityStrategyProvider { + accessibilityItem.modifierType.modify(item: accessibilityItem, generator: header) + } else { + accessibilityItem.modifierType.modify(item: accessibilityItem) + } + } + + private func processTableFooter(_ section: Int, _ view: UIView, with manager: BaseTableManager?) { + guard let accessibilityItem = view as? AccessibilityItem else { + return + } + // AccessibilityStrategyProvider for a footer generator is not supported yet + // TODO: SPT-1468 + accessibilityItem.modifierType.modify(item: accessibilityItem) + } + } extension BaseTablePlugin { From 86d2c9059c8b88659b9626d9da0753b312066391 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Wed, 31 May 2023 17:19:37 +0300 Subject: [PATCH 028/126] refactor, item fixes --- Source/Collection/Protocols/CollectionFoldableItem.swift | 2 +- .../Accessibility/AccessibilityItem+Providers.swift | 6 ++++++ .../Accessibility/AccessibilityItem+Strategies.swift | 5 +++-- .../Generators/Accessibility/AccessibilityItem.swift | 2 -- Source/Protocols/Plugins/Generators/FoldableItem.swift | 4 +--- Source/Protocols/Plugins/Generators/SelectableItem.swift | 2 +- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Source/Collection/Protocols/CollectionFoldableItem.swift b/Source/Collection/Protocols/CollectionFoldableItem.swift index 508aa780d..ab0f84ac7 100644 --- a/Source/Collection/Protocols/CollectionFoldableItem.swift +++ b/Source/Collection/Protocols/CollectionFoldableItem.swift @@ -14,5 +14,5 @@ public protocol CollectionFoldableItem: AnyObject, AccessibilityStrategyProvider } extension CollectionFoldableItem { - public var traitsStrategy: AccessibilityTraitsStrategy { .just(.button) } + public var traitsStrategy: AccessibilityTraitsStrategy { childGenerators.isEmpty ? .ignored : .just(.button) } } diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Providers.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Providers.swift index 6dda4e233..573a06f6a 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Providers.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Providers.swift @@ -32,6 +32,12 @@ public extension AccessibilityStrategyProvider { } } +/// `UIAccessibilityCustomAction`'s provider public protocol AccessibilityActionsProvider { func accessibilityActions() -> [UIAccessibilityCustomAction] } + +/// `AccessibilityItem`default actions +public extension AccessibilityItem { + func accessibilityActions() -> [UIAccessibilityCustomAction] { [] } +} diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Strategies.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Strategies.swift index d8bfc762a..3e2932dcb 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Strategies.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Strategies.swift @@ -10,7 +10,7 @@ import UIKit /// Default accessibility strategy for string value public enum AccessibilityStringStrategy { - /// value will not be change by this strategy + /// value will not be changed by this strategy case ignored /// simple string value, can be nil @@ -31,6 +31,7 @@ public enum AccessibilityStringStrategy { } } + /// value of current strategy public var value: String? { switch self { case .ignored: @@ -48,7 +49,7 @@ public enum AccessibilityStringStrategy { /// Accessibility strategy for `UIAccessibilityTraits` parameter public enum AccessibilityTraitsStrategy { - /// value will not be change by this strategy + /// value will not be changed by this strategy case ignored /// simple accessibility traits diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift index 0190cc546..1a50ce9ff 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift @@ -35,7 +35,5 @@ public extension AccessibilityItem { generatorStrategy: AccessibilityStringStrategy) -> String? { return [generatorStrategy, itemStrategy].compactMap(\.value).joined(separator: " ") } - - func accessibilityActions() -> [UIAccessibilityCustomAction] { [] } } diff --git a/Source/Protocols/Plugins/Generators/FoldableItem.swift b/Source/Protocols/Plugins/Generators/FoldableItem.swift index a287c1953..f7bb0fda5 100644 --- a/Source/Protocols/Plugins/Generators/FoldableItem.swift +++ b/Source/Protocols/Plugins/Generators/FoldableItem.swift @@ -31,11 +31,9 @@ public protocol FoldableItem: AnyObject, AccessibilityStrategyProvider { } public extension FoldableItem { - var animation: TableFoldablePlugin.AnimationGroup { return (.none, .fade) } - var traitsStrategy: AccessibilityTraitsStrategy { .just(.button) } - + var traitsStrategy: AccessibilityTraitsStrategy { childGenerators.isEmpty ? .ignored : .just(.button) } } diff --git a/Source/Protocols/Plugins/Generators/SelectableItem.swift b/Source/Protocols/Plugins/Generators/SelectableItem.swift index e0307c74a..bbc5bbf62 100644 --- a/Source/Protocols/Plugins/Generators/SelectableItem.swift +++ b/Source/Protocols/Plugins/Generators/SelectableItem.swift @@ -24,5 +24,5 @@ public protocol SelectableItem: AnyObject, AccessibilityStrategyProvider { } extension SelectableItem { - public var traitsStrategy: AccessibilityTraitsStrategy { .just(.button) } + public var traitsStrategy: AccessibilityTraitsStrategy { didSelectEvent.isEmpty ? .ignored : .just(.button) } } From ae77ec57143f0d930405b7d5ae7ca414c46078ad Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Wed, 31 May 2023 17:31:58 +0300 Subject: [PATCH 029/126] added comments --- .../ExpandableTableCell.swift | 4 ++-- .../AccessibilityItem+Invalidation.swift | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.swift index 659c4c81f..712837f43 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.swift @@ -20,7 +20,7 @@ class ExpandableTableCell: UITableViewCell, ExpandableItem, AccessibilityInvalid private var isSmall = true { didSet { - accessibilityInvalidator?.invalidateAccessibility() + accessibilityInvalidator?.invalidateAccessibilityParameters() } } @@ -29,7 +29,7 @@ class ExpandableTableCell: UITableViewCell, ExpandableItem, AccessibilityInvalid public var onHeightChanged: BaseEvent = .init() public var animatedExpandable = true { didSet { - accessibilityInvalidator?.invalidateAccessibility() + accessibilityInvalidator?.invalidateAccessibilityParameters() } } diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift index 13d94a488..5349bbe3f 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift @@ -7,28 +7,37 @@ import Foundation +/// Type of `AccessibilityItem` with provided index public enum AccessibilityItemKind { case header(Int), cell(IndexPath), footer(Int) } -public struct AccessibilityItemInvalidator { +public protocol AccessibilityItemInvalidator { + /// Invalidates and sets all accessibility parameters for on-screen item + func invalidateAccessibilityParameters() +} + +struct CommonAccessibilityItemInvalidator: AccessibilityItemInvalidator { let accessibilityItemKind: AccessibilityItemKind weak var item: AccessibilityItem? weak var accessibilityDelegate: AccessibilityItemDelegate? - public func invalidateAccessibility() { + public func invalidateAccessibilityParameters() { guard let item else { return } accessibilityDelegate?.didInvalidateAccessibility(for: item, of: accessibilityItemKind) } } +/// Protocol for invalidation mechanism public protocol AccessibilityInvalidatable: AccessibilityItem { + /// Stored invalidator that can be used for accessibility parameters invalidation. + /// Fully managed by accessibility plugins. This value is `nil` if item isn't displaying var accessibilityInvalidator: AccessibilityItemInvalidator? { get set } } extension AccessibilityInvalidatable { func setInvalidator(kind: AccessibilityItemKind, delegate: AccessibilityItemDelegate?) { - accessibilityInvalidator = AccessibilityItemInvalidator(accessibilityItemKind: kind, item: self, accessibilityDelegate: delegate) + accessibilityInvalidator = CommonAccessibilityItemInvalidator(accessibilityItemKind: kind, item: self, accessibilityDelegate: delegate) } func removeInvalidator() { @@ -36,6 +45,7 @@ extension AccessibilityInvalidatable { } } +/// Invalidation delegate for `CollectionDelegate` or `TableDelegate` public protocol AccessibilityItemDelegate: AnyObject { func didInvalidateAccessibility(for item: AccessibilityItem, of kind: AccessibilityItemKind) } From e5b7b981a6e7f1a5db4f0505318b27911560a900 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Thu, 1 Jun 2023 13:51:05 +0300 Subject: [PATCH 030/126] fixed tests & run sourcery --- .../Collection/CollectionBuilderTests.swift | 2 +- .../Builder/Table/TableBuilderTests.swift | 2 +- .../Mocks/AutoMockable.generated.swift | 40 +++++++++++++++++++ .../Delegate/BaseCollectionDelegate.swift | 2 +- .../Delegate/CollectionDelegate.swift | 2 +- .../CollectionAccessibilityPlugin.swift | 23 ++++++++--- Source/Table/Delegate/BaseTableDelegate.swift | 2 +- Source/Table/Delegate/TableDelegate.swift | 2 +- .../TableAccessibilityPlugin.swift | 24 ++++++++--- project.yml | 2 +- 10 files changed, 82 insertions(+), 19 deletions(-) diff --git a/ReactiveDataDisplayManagerTests/Builder/Collection/CollectionBuilderTests.swift b/ReactiveDataDisplayManagerTests/Builder/Collection/CollectionBuilderTests.swift index 6a8a4dfdf..80cf2aacc 100644 --- a/ReactiveDataDisplayManagerTests/Builder/Collection/CollectionBuilderTests.swift +++ b/ReactiveDataDisplayManagerTests/Builder/Collection/CollectionBuilderTests.swift @@ -28,7 +28,7 @@ class CollectionBuilderTests: XCTestCase { // then XCTAssertTrue(builder.scrollPlugins.plugins.isEmpty) - XCTAssertTrue(builder.collectionPlugins.plugins.isEmpty) + XCTAssertTrue(builder.collectionPlugins.plugins.filter { !($0 is CollectionAccessibilityPlugin) }.isEmpty) XCTAssertTrue(builder.prefetchPlugins.plugins.isEmpty) XCTAssertNil(builder.movablePlugin) diff --git a/ReactiveDataDisplayManagerTests/Builder/Table/TableBuilderTests.swift b/ReactiveDataDisplayManagerTests/Builder/Table/TableBuilderTests.swift index 8b4c6e91d..85ceb3a89 100644 --- a/ReactiveDataDisplayManagerTests/Builder/Table/TableBuilderTests.swift +++ b/ReactiveDataDisplayManagerTests/Builder/Table/TableBuilderTests.swift @@ -28,7 +28,7 @@ class TableBuilderTests: XCTestCase { // then XCTAssertTrue(builder.scrollPlugins.plugins.isEmpty) - XCTAssertTrue(builder.tablePlugins.plugins.isEmpty) + XCTAssertTrue(builder.tablePlugins.plugins.filter { !($0 is TableAccessibilityPlugin) }.isEmpty) XCTAssertTrue(builder.prefetchPlugins.plugins.isEmpty) XCTAssertNil(builder.swipeActionsPlugin) XCTAssertNil(builder.movablePlugin) diff --git a/ReactiveDataDisplayManagerTests/Mocks/AutoMockable.generated.swift b/ReactiveDataDisplayManagerTests/Mocks/AutoMockable.generated.swift index b8181dd1f..cd2b44f22 100644 --- a/ReactiveDataDisplayManagerTests/Mocks/AutoMockable.generated.swift +++ b/ReactiveDataDisplayManagerTests/Mocks/AutoMockable.generated.swift @@ -98,6 +98,26 @@ class CollectionFoldableItemMock: CollectionFoldableItem { } var underlyingIsExpanded: Bool! var childGenerators: [CollectionCellGenerator] = [] + var labelStrategy: AccessibilityStringStrategy { + get { return underlyingLabelStrategy } + set(value) { underlyingLabelStrategy = value } + } + var underlyingLabelStrategy: AccessibilityStringStrategy! + var valueStrategy: AccessibilityStringStrategy { + get { return underlyingValueStrategy } + set(value) { underlyingValueStrategy = value } + } + var underlyingValueStrategy: AccessibilityStringStrategy! + var traitsStrategy: AccessibilityTraitsStrategy { + get { return underlyingTraitsStrategy } + set(value) { underlyingTraitsStrategy = value } + } + var underlyingTraitsStrategy: AccessibilityTraitsStrategy! + var isAccessibilityIgnored: Bool { + get { return underlyingIsAccessibilityIgnored } + set(value) { underlyingIsAccessibilityIgnored = value } + } + var underlyingIsAccessibilityIgnored: Bool! } class CollectionFooterGeneratorMock: CollectionFooterGenerator { @@ -175,6 +195,26 @@ class CollectionHeaderGeneratorMock: CollectionHeaderGenerator { set(value) { underlyingIdentifier = value } } var underlyingIdentifier: UICollectionReusableView.Type! + var labelStrategy: AccessibilityStringStrategy { + get { return underlyingLabelStrategy } + set(value) { underlyingLabelStrategy = value } + } + var underlyingLabelStrategy: AccessibilityStringStrategy! + var valueStrategy: AccessibilityStringStrategy { + get { return underlyingValueStrategy } + set(value) { underlyingValueStrategy = value } + } + var underlyingValueStrategy: AccessibilityStringStrategy! + var traitsStrategy: AccessibilityTraitsStrategy { + get { return underlyingTraitsStrategy } + set(value) { underlyingTraitsStrategy = value } + } + var underlyingTraitsStrategy: AccessibilityTraitsStrategy! + var isAccessibilityIgnored: Bool { + get { return underlyingIsAccessibilityIgnored } + set(value) { underlyingIsAccessibilityIgnored = value } + } + var underlyingIsAccessibilityIgnored: Bool! //MARK: - generate diff --git a/Source/Collection/Delegate/BaseCollectionDelegate.swift b/Source/Collection/Delegate/BaseCollectionDelegate.swift index 268fa64df..ffae558b0 100644 --- a/Source/Collection/Delegate/BaseCollectionDelegate.swift +++ b/Source/Collection/Delegate/BaseCollectionDelegate.swift @@ -200,7 +200,7 @@ extension BaseCollectionDelegate { // MARK: AccessibilityItemDelegate -extension BaseCollectionDelegate { +extension BaseCollectionDelegate: AccessibilityItemDelegate { public func didInvalidateAccessibility(for item: AccessibilityItem, of kind: AccessibilityItemKind) { switch kind { diff --git a/Source/Collection/Delegate/CollectionDelegate.swift b/Source/Collection/Delegate/CollectionDelegate.swift index 08a4e39f2..f2b19061d 100644 --- a/Source/Collection/Delegate/CollectionDelegate.swift +++ b/Source/Collection/Delegate/CollectionDelegate.swift @@ -8,7 +8,7 @@ import UIKit -public protocol CollectionDelegate: CollectionBuilderConfigurable, UICollectionViewDelegate, AccessibilityItemDelegate { +public protocol CollectionDelegate: CollectionBuilderConfigurable, UICollectionViewDelegate { var manager: BaseCollectionManager? { get set } var collectionPlugins: PluginCollection> { get set } var scrollPlugins: PluginCollection> { get set } diff --git a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift index 90d0d2f5a..f338275af 100644 --- a/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift +++ b/Source/Collection/Plugins/PluginAction/CollectionAccessibilityPlugin.swift @@ -13,7 +13,7 @@ final class CollectionAccessibilityPlugin: BaseCollectionPlugin switch event { case let .willDisplayCell(indexPath, cell): processCollectionCell(indexPath, cell, with: manager) - (cell as? AccessibilityInvalidatable)?.setInvalidator(kind: .cell(indexPath), delegate: manager?.delegate) + tryToSetInvalidator(for: cell, of: .cell(indexPath), with: manager) case let .invalidatedCellAccessibility(indexPath, cell): processCollectionCell(indexPath, cell, with: manager) @@ -22,10 +22,10 @@ final class CollectionAccessibilityPlugin: BaseCollectionPlugin switch kind { case UICollectionView.elementKindSectionHeader: processCollectionHeader(indexPath.section, view, with: manager) - (view as? AccessibilityInvalidatable)?.setInvalidator(kind: .header(indexPath.section), delegate: manager?.delegate) + tryToSetInvalidator(for: view, of: .header(indexPath.section), with: manager) case UICollectionView.elementKindSectionFooter: processCollectionFooter(indexPath.section, view, with: manager) - (view as? AccessibilityInvalidatable)?.setInvalidator(kind: .footer(indexPath.section), delegate: manager?.delegate) + tryToSetInvalidator(for: view, of: .footer(indexPath.section), with: manager) default: break } @@ -43,8 +43,19 @@ final class CollectionAccessibilityPlugin: BaseCollectionPlugin break } } +} + +private extension CollectionAccessibilityPlugin { + + func tryToSetInvalidator(for view: UIView, of kind: AccessibilityItemKind, with manager: BaseCollectionManager?) { + guard let invalidatable = view as? AccessibilityInvalidatable, + let invalidateDelegate = manager?.delegate as? AccessibilityItemDelegate else { + return + } + invalidatable.setInvalidator(kind: kind, delegate: invalidateDelegate) + } - private func processCollectionCell(_ indexPath: IndexPath, _ cell: UICollectionViewCell, with manager: BaseCollectionManager?) { + func processCollectionCell(_ indexPath: IndexPath, _ cell: UICollectionViewCell, with manager: BaseCollectionManager?) { guard let accessibilityItem = cell as? AccessibilityItem else { return } @@ -55,7 +66,7 @@ final class CollectionAccessibilityPlugin: BaseCollectionPlugin } } - private func processCollectionHeader(_ section: Int, _ view: UICollectionReusableView, with manager: BaseCollectionManager?) { + func processCollectionHeader(_ section: Int, _ view: UICollectionReusableView, with manager: BaseCollectionManager?) { guard let accessibilityItem = view as? AccessibilityItem else { return } @@ -66,7 +77,7 @@ final class CollectionAccessibilityPlugin: BaseCollectionPlugin } } - private func processCollectionFooter(_ section: Int, _ view: UICollectionReusableView, with manager: BaseCollectionManager?) { + func processCollectionFooter(_ section: Int, _ view: UICollectionReusableView, with manager: BaseCollectionManager?) { guard let accessibilityItem = view as? AccessibilityItem else { return } diff --git a/Source/Table/Delegate/BaseTableDelegate.swift b/Source/Table/Delegate/BaseTableDelegate.swift index 27978892e..89913909f 100644 --- a/Source/Table/Delegate/BaseTableDelegate.swift +++ b/Source/Table/Delegate/BaseTableDelegate.swift @@ -260,7 +260,7 @@ extension BaseTableDelegate: TableDragAndDropDelegate { // MARK: AccessibilityItemDelegate -extension BaseTableDelegate { +extension BaseTableDelegate: AccessibilityItemDelegate { public func didInvalidateAccessibility(for item: AccessibilityItem, of kind: AccessibilityItemKind) { switch kind { diff --git a/Source/Table/Delegate/TableDelegate.swift b/Source/Table/Delegate/TableDelegate.swift index 9c606126c..fc8f31d0f 100644 --- a/Source/Table/Delegate/TableDelegate.swift +++ b/Source/Table/Delegate/TableDelegate.swift @@ -8,7 +8,7 @@ import UIKit -public protocol TableDelegate: TableBuilderConfigurable, UITableViewDelegate, AccessibilityItemDelegate { +public protocol TableDelegate: TableBuilderConfigurable, UITableViewDelegate { var manager: BaseTableManager? { get set } var tablePlugins: PluginCollection> { get set } diff --git a/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift b/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift index 2a2a02efa..60eeb0312 100644 --- a/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift +++ b/Source/Table/Plugins/PluginAction/TableAccessibilityPlugin.swift @@ -15,21 +15,21 @@ final class TableAccessibilityPlugin: BaseTablePlugin { switch event { case let .willDisplayCell(indexPath, cell): processTableCell(indexPath, cell, with: manager) - (cell as? AccessibilityInvalidatable)?.setInvalidator(kind: .cell(indexPath), delegate: manager?.delegate) + tryToSetInvalidator(for: cell, of: .cell(indexPath), with: manager) case let .invalidatedCellAccessibility(indexPath, cell): processTableCell(indexPath, cell, with: manager) case let .willDisplayHeader(section, view): processTableHeader(section, view, with: manager) - (view as? AccessibilityInvalidatable)?.setInvalidator(kind: .header(section), delegate: manager?.delegate) + tryToSetInvalidator(for: view, of: .header(section), with: manager) case let .invalidatedHeaderAccessibility(section, view): processTableHeader(section, view, with: manager) case let .willDisplayFooter(section, view): processTableFooter(section, view, with: manager) - (view as? AccessibilityInvalidatable)?.setInvalidator(kind: .footer(section), delegate: manager?.delegate) + tryToSetInvalidator(for: view, of: .footer(section), with: manager) case let .invalidatedFooterAccessibility(section, view): processTableFooter(section, view, with: manager) @@ -42,7 +42,19 @@ final class TableAccessibilityPlugin: BaseTablePlugin { } } - private func processTableCell(_ indexPath: IndexPath, _ cell: UITableViewCell, with manager: BaseTableManager?) { +} + +private extension TableAccessibilityPlugin { + + func tryToSetInvalidator(for view: UIView, of kind: AccessibilityItemKind, with manager: BaseTableManager?) { + guard let invalidatable = view as? AccessibilityInvalidatable, + let invalidateDelegate = manager?.delegate as? AccessibilityItemDelegate else { + return + } + invalidatable.setInvalidator(kind: kind, delegate: invalidateDelegate) + } + + func processTableCell(_ indexPath: IndexPath, _ cell: UITableViewCell, with manager: BaseTableManager?) { guard let accessibilityItem = cell as? AccessibilityItem else { return } @@ -53,7 +65,7 @@ final class TableAccessibilityPlugin: BaseTablePlugin { } } - private func processTableHeader(_ section: Int, _ view: UIView, with manager: BaseTableManager?) { + func processTableHeader(_ section: Int, _ view: UIView, with manager: BaseTableManager?) { guard let accessibilityItem = view as? AccessibilityItem else { return } @@ -64,7 +76,7 @@ final class TableAccessibilityPlugin: BaseTablePlugin { } } - private func processTableFooter(_ section: Int, _ view: UIView, with manager: BaseTableManager?) { + func processTableFooter(_ section: Int, _ view: UIView, with manager: BaseTableManager?) { guard let accessibilityItem = view as? AccessibilityItem else { return } diff --git a/project.yml b/project.yml index 1905b9aca..2581a1b32 100644 --- a/project.yml +++ b/project.yml @@ -60,7 +60,7 @@ targets: dependencies: - target: ReactiveDataDisplayManager_${platform} preBuildScripts: - - script: sourcery + - script: source ~/.bash_profile sourcery name: Sourcery settings: base: From e0eb1fe3b639ff20a5c474b3987213732d01a732 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Thu, 1 Jun 2023 14:04:33 +0300 Subject: [PATCH 031/126] added marks for events --- Source/Collection/Events/CollectionEvent.swift | 4 +++- Source/Table/Events/TableEvent.swift | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/Collection/Events/CollectionEvent.swift b/Source/Collection/Events/CollectionEvent.swift index 0c03dbf21..2fbe5698a 100644 --- a/Source/Collection/Events/CollectionEvent.swift +++ b/Source/Collection/Events/CollectionEvent.swift @@ -17,8 +17,10 @@ public enum CollectionEvent { case didEndDisplayCell(IndexPath, UICollectionViewCell) case willDisplaySupplementaryView(IndexPath, UICollectionReusableView, String) case didEndDisplayingSupplementaryView(IndexPath, UICollectionReusableView, String) + case move(from: IndexPath, to: IndexPath) + + // MARK: - Accessibility Events case invalidatedCellAccessibility(IndexPath, UICollectionViewCell) case invalidatedHeaderAccessibility(Int, UICollectionReusableView) case invalidatedFooterAccessibility(Int, UICollectionReusableView) - case move(from: IndexPath, to: IndexPath) } diff --git a/Source/Table/Events/TableEvent.swift b/Source/Table/Events/TableEvent.swift index 1883c55c2..0bf0f8093 100644 --- a/Source/Table/Events/TableEvent.swift +++ b/Source/Table/Events/TableEvent.swift @@ -24,9 +24,11 @@ public enum TableEvent { case didEndDisplayHeader(Int, UIView) case willDisplayFooter(Int, UIView) case didEndDisplayFooter(Int, UIView) + case didUpdateFocus(context: UITableViewFocusUpdateContext, coordinator: UIFocusAnimationCoordinator) + case move(from: IndexPath, to: IndexPath) + + // MARK: - Accessibility Events case invalidatedCellAccessibility(IndexPath, UITableViewCell) case invalidatedHeaderAccessibility(Int, UIView) case invalidatedFooterAccessibility(Int, UIView) - case didUpdateFocus(context: UITableViewFocusUpdateContext, coordinator: UIFocusAnimationCoordinator) - case move(from: IndexPath, to: IndexPath) } From 10eda755b41f72e2372c6a5b90cf16829d0cdc8d Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Thu, 1 Jun 2023 14:51:45 +0300 Subject: [PATCH 032/126] renamed parameter --- .../Accessibility/AccessibilityItem+Invalidation.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift index 5349bbe3f..096a2fbab 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Invalidation.swift @@ -14,7 +14,7 @@ public enum AccessibilityItemKind { public protocol AccessibilityItemInvalidator { /// Invalidates and sets all accessibility parameters for on-screen item - func invalidateAccessibilityParameters() + func invalidateParameters() } struct CommonAccessibilityItemInvalidator: AccessibilityItemInvalidator { @@ -22,7 +22,7 @@ struct CommonAccessibilityItemInvalidator: AccessibilityItemInvalidator { weak var item: AccessibilityItem? weak var accessibilityDelegate: AccessibilityItemDelegate? - public func invalidateAccessibilityParameters() { + public func invalidateParameters() { guard let item else { return } accessibilityDelegate?.didInvalidateAccessibility(for: item, of: accessibilityItemKind) } From 19274c21c6431ca9f2a5d6b91c2f62fd0e1f0f40 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Thu, 1 Jun 2023 19:06:20 +0300 Subject: [PATCH 033/126] removed default strategies --- Source/Collection/Protocols/CollectionFoldableItem.swift | 5 +++-- .../Accessibility/AccessibilityItem+Providers.swift | 6 ++---- Source/Protocols/Plugins/Generators/FoldableItem.swift | 1 + Source/Protocols/Plugins/Generators/SelectableItem.swift | 1 + Source/Protocols/Protocols.swift | 4 +++- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Source/Collection/Protocols/CollectionFoldableItem.swift b/Source/Collection/Protocols/CollectionFoldableItem.swift index ab0f84ac7..03b5a5881 100644 --- a/Source/Collection/Protocols/CollectionFoldableItem.swift +++ b/Source/Collection/Protocols/CollectionFoldableItem.swift @@ -13,6 +13,7 @@ public protocol CollectionFoldableItem: AnyObject, AccessibilityStrategyProvider var childGenerators: [CollectionCellGenerator] { get set } } -extension CollectionFoldableItem { - public var traitsStrategy: AccessibilityTraitsStrategy { childGenerators.isEmpty ? .ignored : .just(.button) } +public extension CollectionFoldableItem { + var labelStrategy: AccessibilityStringStrategy { .ignored } + var traitsStrategy: AccessibilityTraitsStrategy { childGenerators.isEmpty ? .ignored : .just(.button) } } diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Providers.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Providers.swift index 573a06f6a..7de6f6a5e 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Providers.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Providers.swift @@ -10,13 +10,13 @@ import UIKit /// Accessibility strategies provider protocol for generators public protocol AccessibilityStrategyProvider { - /// strategy for `accessibilityLabel`. Default: `.ignored` + /// strategy for `accessibilityLabel` var labelStrategy: AccessibilityStringStrategy { get } /// strategy for `accessibilityValue`. Default: `.ignored` var valueStrategy: AccessibilityStringStrategy { get } - /// strategy for `accessibilityTraits`. Default: `.ignored` + /// strategy for `accessibilityTraits` var traitsStrategy: AccessibilityTraitsStrategy { get } /// Idicates that `AccessibilityItem` should become an accessibility element. Equals `true` if all strategies is in state `.ignored` @@ -24,9 +24,7 @@ public protocol AccessibilityStrategyProvider { } public extension AccessibilityStrategyProvider { - var labelStrategy: AccessibilityStringStrategy { .ignored } var valueStrategy: AccessibilityStringStrategy { .ignored } - var traitsStrategy: AccessibilityTraitsStrategy { .ignored } var isAccessibilityIgnored: Bool { return [labelStrategy.isIgnored, valueStrategy.isIgnored, traitsStrategy.isIgnored].allSatisfy { $0 } } diff --git a/Source/Protocols/Plugins/Generators/FoldableItem.swift b/Source/Protocols/Plugins/Generators/FoldableItem.swift index f7bb0fda5..44457ad90 100644 --- a/Source/Protocols/Plugins/Generators/FoldableItem.swift +++ b/Source/Protocols/Plugins/Generators/FoldableItem.swift @@ -35,5 +35,6 @@ public extension FoldableItem { return (.none, .fade) } + var labelStrategy: AccessibilityStringStrategy { .ignored } var traitsStrategy: AccessibilityTraitsStrategy { childGenerators.isEmpty ? .ignored : .just(.button) } } diff --git a/Source/Protocols/Plugins/Generators/SelectableItem.swift b/Source/Protocols/Plugins/Generators/SelectableItem.swift index bbc5bbf62..352e6eafc 100644 --- a/Source/Protocols/Plugins/Generators/SelectableItem.swift +++ b/Source/Protocols/Plugins/Generators/SelectableItem.swift @@ -24,5 +24,6 @@ public protocol SelectableItem: AnyObject, AccessibilityStrategyProvider { } extension SelectableItem { + public var labelStrategy: AccessibilityStringStrategy { .ignored } public var traitsStrategy: AccessibilityTraitsStrategy { didSelectEvent.isEmpty ? .ignored : .just(.button) } } diff --git a/Source/Protocols/Protocols.swift b/Source/Protocols/Protocols.swift index 6750ace76..d0fc4baba 100644 --- a/Source/Protocols/Protocols.swift +++ b/Source/Protocols/Protocols.swift @@ -13,7 +13,8 @@ open class TableHeaderGenerator: ViewGenerator, AccessibilityStrategyProvider { public let uuid = UUID().uuidString - open var traitsStrategy: AccessibilityTraitsStrategy { .just(.header) } + open var labelStrategy: AccessibilityStringStrategy = .ignored + open var traitsStrategy: AccessibilityTraitsStrategy = .just(.header) public init() { } @@ -92,6 +93,7 @@ public extension CollectionHeaderGenerator { return nil } + var labelStrategy: AccessibilityStringStrategy { .ignored } var traitsStrategy: AccessibilityTraitsStrategy { .just(.header) } } From 840200705f8e5332cb69061f4d3effbf6fce7ebf Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Thu, 1 Jun 2023 19:28:05 +0300 Subject: [PATCH 034/126] added table views accessibility --- .../RectangleColorCollectionViewCell.swift | 1 + ...tingCompressedSizeCollectionViewCell.swift | 1 + .../FoldableCollectionViewCell.swift | 3 +- .../SizableCollectionViewCell.swift | 1 + .../TitleCollectionListCell.swift | 1 + .../TitleCollectionViewCell.swift | 32 +++++++++++-------- .../TitleIconCollectionFooterView.swift | 1 + .../HeaderCollectionListView.swift | 1 + .../TitleCollectionReusableView.swift | 1 + .../ExpandableTableCell.swift | 4 +-- .../FoldableTableViewCell.swift | 29 +++++++++++++++-- .../HighlightableTableCell.swift | 30 ++++++++++------- .../ImageTableViewCell.swift | 10 ++++++ .../TitleTableViewCell.swift | 15 +++++---- .../TitleWithIconTableViewCell.swift | 16 ++++++---- .../Views/Headers/HeaderView/HeaderView.swift | 8 ++++- .../TitleTableViewCell.swift | 9 ++++++ .../Headers/TitleCollectionReusableView.swift | 1 + .../Plugins/FoldablePluginExampleUITest.swift | 2 +- .../HighlightablePluginExampleUITest.swift | 8 ++--- 20 files changed, 126 insertions(+), 48 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/DynamicHeightViewCell/Subviews/RectangleColorCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/DynamicHeightViewCell/Subviews/RectangleColorCollectionViewCell.swift index 33a4e2f06..8f12a8050 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/DynamicHeightViewCell/Subviews/RectangleColorCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/DynamicHeightViewCell/Subviews/RectangleColorCollectionViewCell.swift @@ -17,6 +17,7 @@ final class RectangleColorCollectionViewCell: UICollectionViewCell, Configurable // MARK: - AccessibilityItem var labelStrategy: AccessibilityStringStrategy = .ignored + var traitsStrategy: AccessibilityTraitsStrategy { .just(.none) } // MARK: - ConfigurableItem diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FittingCompressedSizeCollectionViewCell/FittingCompressedSizeCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FittingCompressedSizeCollectionViewCell/FittingCompressedSizeCollectionViewCell.swift index 3fc55fe54..9e49df59f 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FittingCompressedSizeCollectionViewCell/FittingCompressedSizeCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FittingCompressedSizeCollectionViewCell/FittingCompressedSizeCollectionViewCell.swift @@ -53,6 +53,7 @@ extension FittingCompressedSizeCollectionViewCell: ConfigurableItem { extension FittingCompressedSizeCollectionViewCell: AccessibilityItem { var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } } diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FoldableCollectionViewCell/FoldableCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FoldableCollectionViewCell/FoldableCollectionViewCell.swift index 716eecaaa..95a19a28d 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FoldableCollectionViewCell/FoldableCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FoldableCollectionViewCell/FoldableCollectionViewCell.swift @@ -61,7 +61,8 @@ extension FoldableCollectionViewCell: ConfigurableItem { extension FoldableCollectionViewCell: AccessibilityItem { var labelStrategy: AccessibilityStringStrategy { .just("Expandable item") } - + var traitsStrategy: AccessibilityTraitsStrategy { .just(.none) } + } // MARK: - Configuration diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.swift index aa16e97bd..754a23027 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.swift @@ -52,6 +52,7 @@ extension SizableCollectionViewCell: ConfigurableItem { extension SizableCollectionViewCell: AccessibilityItem { var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } } diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionListCell/TitleCollectionListCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionListCell/TitleCollectionListCell.swift index 97a3a7d0e..65ebbf28a 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionListCell/TitleCollectionListCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionListCell/TitleCollectionListCell.swift @@ -18,6 +18,7 @@ class TitleCollectionListCell: UICollectionViewCell, ConfigurableItem, Accessibi // MARK: - AccessibilityItem var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } // MARK: - Configurable diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift index 9f406ca7e..bd069e45f 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift @@ -9,12 +9,26 @@ import UIKit import ReactiveDataDisplayManager -final class TitleCollectionViewCell: UICollectionViewCell { +final class TitleCollectionViewCell: UICollectionViewCell, AccessibilityInvalidatable { // MARK: - IBOutlets @IBOutlet private weak var titleLabel: UILabel! + // MARK: - AccessibilityInvalidatable + + var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + var valueStrategy: AccessibilityStringStrategy = .just(nil) { + didSet { + accessibilityInvalidator?.invalidateParameters() + } + } + var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } + + var accessibilityInvalidator: AccessibilityItemInvalidator? + + // MARK: - UICollectionViewCell + override func awakeFromNib() { super.awakeFromNib() configureAppearance() @@ -32,37 +46,29 @@ extension TitleCollectionViewCell: ConfigurableItem { } -// MARK: - AccessibilityItem - -extension TitleCollectionViewCell: AccessibilityItem { - - var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } - -} - // MARK: - HighlightableItem extension TitleCollectionViewCell: HighlightableItem { func applyUnhighlightedStyle() { contentView.backgroundColor = .gray - accessibilityValue = "Normal" + valueStrategy = .just("Normal") } func applyHighlightedStyle() { contentView.backgroundColor = .white.withAlphaComponent(0.5) - accessibilityValue = "Highlighted" + valueStrategy = .just("Highlighted") } func applySelectedStyle() { contentView.layer.borderColor = UIColor.blue.cgColor contentView.layer.borderWidth = 1 - accessibilityValue = "Selected" + valueStrategy = .just("Selected") } func applyDeselectedStyle() { contentView.layer.borderWidth = .zero - accessibilityValue = "Normal" + valueStrategy = .just("Normal") } } diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Footer/TitleIconCollectionFooterView.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Footer/TitleIconCollectionFooterView.swift index 1a24769b8..8ad08aa8e 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Footer/TitleIconCollectionFooterView.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Footer/TitleIconCollectionFooterView.swift @@ -18,6 +18,7 @@ class TitleIconCollectionFooterView: UICollectionReusableView, AccessibilityItem // MARK: - AccessibilityItem var labelStrategy: AccessibilityStringStrategy { .joined([.just("some image with title: "), .from(object: titleLabel)]) } + var traitsStrategy: AccessibilityTraitsStrategy { .merge([iconImageView, titleLabel]) } // MARK: - Internal methods diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/HeaderCollectionListView/HeaderCollectionListView.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/HeaderCollectionListView/HeaderCollectionListView.swift index 38b292ab7..ba2c596d4 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/HeaderCollectionListView/HeaderCollectionListView.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/HeaderCollectionListView/HeaderCollectionListView.swift @@ -18,6 +18,7 @@ final class HeaderCollectionListView: UICollectionReusableView, AccessibilityIte // MARK: - AccessibilityItem var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } // MARK: - Internal Methods diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/TitleCollectionReusableView/TitleCollectionReusableView.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/TitleCollectionReusableView/TitleCollectionReusableView.swift index 3dc010394..19323d18b 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/TitleCollectionReusableView/TitleCollectionReusableView.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/TitleCollectionReusableView/TitleCollectionReusableView.swift @@ -18,6 +18,7 @@ final class TitleCollectionReusableView: UICollectionReusableView, Accessibility // MARK: - AccessibilityItem var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } // MARK: - Internal methods diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.swift index 712837f43..4145f51f0 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.swift @@ -20,7 +20,7 @@ class ExpandableTableCell: UITableViewCell, ExpandableItem, AccessibilityInvalid private var isSmall = true { didSet { - accessibilityInvalidator?.invalidateAccessibilityParameters() + accessibilityInvalidator?.invalidateParameters() } } @@ -29,7 +29,7 @@ class ExpandableTableCell: UITableViewCell, ExpandableItem, AccessibilityInvalid public var onHeightChanged: BaseEvent = .init() public var animatedExpandable = true { didSet { - accessibilityInvalidator?.invalidateAccessibilityParameters() + accessibilityInvalidator?.invalidateParameters() } } diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/FoldableTableViewCell/FoldableTableViewCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/FoldableTableViewCell/FoldableTableViewCell.swift index 676374a1c..d4b4dce17 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/FoldableTableViewCell/FoldableTableViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/FoldableTableViewCell/FoldableTableViewCell.swift @@ -9,7 +9,7 @@ import UIKit import ReactiveDataDisplayManager -final class FoldableTableViewCell: UITableViewCell, FoldableStateHolder { +final class FoldableTableViewCell: UITableViewCell, FoldableStateHolder, AccessibilityInvalidatable { struct Model { let title: String @@ -28,6 +28,18 @@ final class FoldableTableViewCell: UITableViewCell, FoldableStateHolder { @IBOutlet private weak var titleLabel: UILabel! @IBOutlet private weak var arrowImageView: UIImageView! + // MARK: - Private Properties + + private var isExpanded: Bool = false { + didSet { + accessibilityInvalidator?.invalidateParameters() + } + } + + // MARK: - AccessibilityInvalidatable + + var accessibilityInvalidator: AccessibilityItemInvalidator? + // MARK: - UITableViewCell override func awakeFromNib() { @@ -38,6 +50,7 @@ final class FoldableTableViewCell: UITableViewCell, FoldableStateHolder { // MARK: - Foldable func setExpanded(_ isExpanded: Bool) { + self.isExpanded = isExpanded UIView.animate(withDuration: Constants.animationDuration) { [weak self] in self?.arrowImageView.transform = isExpanded ? .identity : CGAffineTransform(rotationAngle: .pi) } @@ -45,14 +58,24 @@ final class FoldableTableViewCell: UITableViewCell, FoldableStateHolder { } -// MARK: - Configurable +// MARK: - AccessibilityItem + +extension FoldableTableViewCell: AccessibilityItem { + + var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + var valueStrategy: AccessibilityStringStrategy { .just("isExpanded: \(isExpanded)") } + var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } + +} + +// MARK: - ConfigurableItem extension FoldableTableViewCell: ConfigurableItem { func configure(with model: Model) { titleLabel.text = String(format: "Foldable cell %@", model.title) - accessibilityLabel = Constants.titleLabelText + model.title arrowImageView.transform = model.isExpanded ? .identity : CGAffineTransform(rotationAngle: .pi) + isExpanded = model.isExpanded } } diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.swift index 9574d4881..137e6e24e 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.swift @@ -8,21 +8,29 @@ import ReactiveDataDisplayManager import UIKit -final class HighlightableTableCell: UITableViewCell { +final class HighlightableTableCell: UITableViewCell, AccessibilityInvalidatable { // MARK: - IBOutlets @IBOutlet private weak var titleLabel: UILabel! - override func awakeFromNib() { - super.awakeFromNib() - configureAppearance() + // MARK: - AccessibilityInvalidatable + + var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + var valueStrategy: AccessibilityStringStrategy = .just(nil) { + didSet { + accessibilityInvalidator?.invalidateParameters() + } } + var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } - // MARK: - Internal methods + var accessibilityInvalidator: AccessibilityItemInvalidator? - func fill(with title: String) { - titleLabel.text = title + // MARK: - AccessibilityInvalidatable + + override func awakeFromNib() { + super.awakeFromNib() + configureAppearance() } } @@ -43,23 +51,23 @@ extension HighlightableTableCell: HighlightableItem { func applyUnhighlightedStyle() { contentView.backgroundColor = .white - accessibilityLabel = "Normal" + valueStrategy = .just("Normal") } func applyHighlightedStyle() { contentView.backgroundColor = .red.withAlphaComponent(0.3) - accessibilityLabel = "Highlighted" + valueStrategy = .just("Highlighted") } func applySelectedStyle() { contentView.layer.borderColor = UIColor.blue.cgColor contentView.layer.borderWidth = 1 - accessibilityLabel = "Selected" + valueStrategy = .just("Selected") } func applyDeselectedStyle() { contentView.layer.borderWidth = .zero - accessibilityLabel = "Normal" + valueStrategy = .just("Normal") } } diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.swift index fc3e700f2..b6dafcc09 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.swift @@ -47,6 +47,16 @@ final class ImageTableViewCell: UITableViewCell { } +// MARK: - AccessibilityItem + +extension ImageTableViewCell: AccessibilityItem { + + var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + var traitsStrategy: AccessibilityTraitsStrategy { .merge([iconView, titleLabel]) } + +} + + // MARK: - ConfigurableItem extension ImageTableViewCell: ConfigurableItem { diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleTableViewCell/TitleTableViewCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleTableViewCell/TitleTableViewCell.swift index 1a8c384e7..14aac93d5 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleTableViewCell/TitleTableViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleTableViewCell/TitleTableViewCell.swift @@ -15,12 +15,6 @@ class TitleTableViewCell: UITableViewCell, CalculatableHeightItem { @IBOutlet private weak var titleLabel: UILabel! - // MARK: - Internal methods - - func fill(with title: String) { - titleLabel.text = title - } - // MARK: - CalculatableHeightItem static func getHeight(forWidth width: CGFloat, with model: String) -> CGFloat { @@ -29,6 +23,15 @@ class TitleTableViewCell: UITableViewCell, CalculatableHeightItem { } +// MARK: - AccessibilityItem + +extension TitleTableViewCell: AccessibilityItem { + + var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } + +} + // MARK: - ConfigurableItem extension TitleTableViewCell: ConfigurableItem { diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleWithIconTableViewCell/TitleWithIconTableViewCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleWithIconTableViewCell/TitleWithIconTableViewCell.swift index 79314ce5d..631231b78 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleWithIconTableViewCell/TitleWithIconTableViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleWithIconTableViewCell/TitleWithIconTableViewCell.swift @@ -15,12 +15,6 @@ class TitleWithIconTableViewCell: UITableViewCell, CalculatableHeightItem { @IBOutlet private weak var iconImageView: UIImageView! @IBOutlet private weak var titleLabel: UILabel! - // MARK: - Internal methods - - func fill(with title: String) { - titleLabel.text = title - } - // MARK: - CalculatableHeightItem static func getHeight(forWidth width: CGFloat, with model: String) -> CGFloat { @@ -29,6 +23,16 @@ class TitleWithIconTableViewCell: UITableViewCell, CalculatableHeightItem { } +// MARK: - AccessibilityItem + +extension TitleWithIconTableViewCell: AccessibilityItem { + + var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + var traitsStrategy: AccessibilityTraitsStrategy { .merge([iconImageView, titleLabel]) } + +} + + // MARK: - ConfigurableItem extension TitleWithIconTableViewCell: ConfigurableItem { diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Headers/HeaderView/HeaderView.swift b/Example/ReactiveDataDisplayManager/Table/Views/Headers/HeaderView/HeaderView.swift index d4a3aed38..9aca8bcef 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Headers/HeaderView/HeaderView.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Headers/HeaderView/HeaderView.swift @@ -7,6 +7,7 @@ // import UIKit +import ReactiveDataDisplayManager extension UIView { @@ -19,12 +20,17 @@ extension UIView { } -final class HeaderView: UIView { +final class HeaderView: UIView, AccessibilityItem { // MARK: - IBOutlet @IBOutlet private weak var titleLabel: UILabel! + // MARK: - AccessibilityItem + + var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } + // MARK: - Internal Methods func configure(with title: String) { diff --git a/Example/ReactiveDataDisplayManagerExampleTv/Views/Cells/TitleTableViewCell/TitleTableViewCell.swift b/Example/ReactiveDataDisplayManagerExampleTv/Views/Cells/TitleTableViewCell/TitleTableViewCell.swift index 6653bd397..bdb92cf31 100644 --- a/Example/ReactiveDataDisplayManagerExampleTv/Views/Cells/TitleTableViewCell/TitleTableViewCell.swift +++ b/Example/ReactiveDataDisplayManagerExampleTv/Views/Cells/TitleTableViewCell/TitleTableViewCell.swift @@ -25,6 +25,15 @@ final class TitleTableViewCell: UITableViewCell { } +// MARK: - AccessibilityItem + +extension TitleTableViewCell: AccessibilityItem { + + var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } + +} + // MARK: - ConfigurableItem extension TitleTableViewCell: ConfigurableItem { diff --git a/Example/ReactiveDataDisplayManagerExampleTv/Views/Headers/TitleCollectionReusableView.swift b/Example/ReactiveDataDisplayManagerExampleTv/Views/Headers/TitleCollectionReusableView.swift index 983246906..079a3ce4a 100644 --- a/Example/ReactiveDataDisplayManagerExampleTv/Views/Headers/TitleCollectionReusableView.swift +++ b/Example/ReactiveDataDisplayManagerExampleTv/Views/Headers/TitleCollectionReusableView.swift @@ -17,6 +17,7 @@ final class TitleCollectionReusableView: UICollectionReusableView, Accessibility // MARK: - AccessibilityItem var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } + var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } // MARK: - Internal methods diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/FoldablePluginExampleUITest.swift b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/FoldablePluginExampleUITest.swift index d01ab6e50..b78069a3c 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/FoldablePluginExampleUITest.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/FoldablePluginExampleUITest.swift @@ -15,7 +15,7 @@ final class FoldablePluginExampleUITest: BaseUITestCase { setTab("Table") tapTableElement("Table with foldable cell") - let foldable = getCell(for: .table, collectionId: tableId, cellId: "Foldable cell1") + let foldable = getCell(for: .table, collectionId: tableId, cellId: "Foldable cell 1") foldable.tap() XCTAssertTrue(getCell(for: .table, collectionId: tableId, cellId: "First subcell").exists) diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/HighlightablePluginExampleUITest.swift b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/HighlightablePluginExampleUITest.swift index b3f767c8f..a5527d475 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/HighlightablePluginExampleUITest.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/HighlightablePluginExampleUITest.swift @@ -22,14 +22,14 @@ final class HighlightablePluginExampleUITest: BaseUITestCase { let cell = getFirstCell(for: .table, id: "Higlighted_cells") DispatchQueue.main.asyncAfter(deadline: .now() + timeout) { - wasPresed = cell.label == highlightedStyle + wasPresed = cell.stringValue == highlightedStyle expectation.fulfill() } cell.press(forDuration: 2) wait(for: [expectation], timeout: timeout) XCTAssertTrue(wasPresed) - XCTAssertEqual(cell.label, normalStyle) + XCTAssertEqual(cell.stringValue, normalStyle) } func testTable_whenCellTaped_thenTurnedSelectStyleAndDeselectStyle() throws { @@ -43,11 +43,11 @@ final class HighlightablePluginExampleUITest: BaseUITestCase { let cell = getFirstCell(for: .table, id: "Higlighted_cells") cell.tap() - XCTAssertEqual(cell.label, selectedStyle) + XCTAssertEqual(cell.stringValue, selectedStyle) XCTAssertTrue(cell.isSelected) cell.tap() - XCTAssertEqual(cell.label, normalStyle) + XCTAssertEqual(cell.stringValue, normalStyle) XCTAssertFalse(cell.isSelected) } From c307dfe3c601285ad38276666e3ff974fdd5895e Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Fri, 2 Jun 2023 11:20:28 +0400 Subject: [PATCH 035/126] disable traits override for UI tests --- .../AccessibilityItem+CommandLine.swift | 41 +++++++++++++++++++ .../BaseClasses/BaseUITestCase.swift | 1 + 2 files changed, 42 insertions(+) create mode 100644 Example/ReactiveDataDisplayManager/Extensions/AccessibilityItem+CommandLine.swift diff --git a/Example/ReactiveDataDisplayManager/Extensions/AccessibilityItem+CommandLine.swift b/Example/ReactiveDataDisplayManager/Extensions/AccessibilityItem+CommandLine.swift new file mode 100644 index 000000000..1d80b2118 --- /dev/null +++ b/Example/ReactiveDataDisplayManager/Extensions/AccessibilityItem+CommandLine.swift @@ -0,0 +1,41 @@ +// +// AccessibilityItem+CommandLine.swift +// ReactiveDataDisplayManagerExample_iOS +// +// Created by Никита Коробейников on 02.06.2023. +// + +import Foundation +import ReactiveDataDisplayManager + +extension AccessibilityItem { + var modifierType: AccessibilityModifierType { + #if DEBUG + if CommandLine.arguments.contains("-doNotOverrideTraits") { + return NoTraitsAccessibilityModifier.self + } else { + return BaseAccessibilityModifier.self + } + #else + return BaseAccessibilityModifier.self + #endif + } +} + +// MARK: - NoTraitsAccessibilityModifier + +enum NoTraitsAccessibilityModifier: AccessibilityModifier { + + static func modify(item: AccessibilityItem) { + let traits = item.accessibilityTraits + BaseAccessibilityModifier.self.modify(item: item) + item.accessibilityTraits = traits + } + + static func modify(item: AccessibilityItem, generator: AccessibilityStrategyProvider) { + let traits = item.accessibilityTraits + BaseAccessibilityModifier.self.modify(item: item, generator: generator) + item.accessibilityTraits = traits + } + +} diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/BaseClasses/BaseUITestCase.swift b/Example/ReactiveDataDisplayManagerExampleUITests/BaseClasses/BaseUITestCase.swift index e7686da49..5509f2a17 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/BaseClasses/BaseUITestCase.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/BaseClasses/BaseUITestCase.swift @@ -23,6 +23,7 @@ open class BaseUITestCase: XCTestCase { continueAfterFailure = false app = XCUIApplication() app.launchArguments.append("-disableAnimations") + app.launchArguments.append("-doNotOverrideTraits") app.launchArguments.append(contentsOf: additionalCommands) app.launch() } From 245c26b191257b7324762e4c0859f6488a3ed68f Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Fri, 2 Jun 2023 11:33:32 +0400 Subject: [PATCH 036/126] revert accessibility value updates with selected/highlighted states --- .../TitleCollectionViewCell.swift | 16 ++++++---------- .../HighlightableTableCell.swift | 18 ++++++------------ 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift index bd069e45f..f2bc818e0 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift @@ -9,7 +9,7 @@ import UIKit import ReactiveDataDisplayManager -final class TitleCollectionViewCell: UICollectionViewCell, AccessibilityInvalidatable { +final class TitleCollectionViewCell: UICollectionViewCell { // MARK: - IBOutlets @@ -18,11 +18,7 @@ final class TitleCollectionViewCell: UICollectionViewCell, AccessibilityInvalida // MARK: - AccessibilityInvalidatable var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } - var valueStrategy: AccessibilityStringStrategy = .just(nil) { - didSet { - accessibilityInvalidator?.invalidateParameters() - } - } + var valueStrategy: AccessibilityStringStrategy = .ignored var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } var accessibilityInvalidator: AccessibilityItemInvalidator? @@ -52,23 +48,23 @@ extension TitleCollectionViewCell: HighlightableItem { func applyUnhighlightedStyle() { contentView.backgroundColor = .gray - valueStrategy = .just("Normal") + accessibilityValue = "Normal" } func applyHighlightedStyle() { contentView.backgroundColor = .white.withAlphaComponent(0.5) - valueStrategy = .just("Highlighted") + accessibilityValue = "Highlighted" } func applySelectedStyle() { contentView.layer.borderColor = UIColor.blue.cgColor contentView.layer.borderWidth = 1 - valueStrategy = .just("Selected") + accessibilityValue = "Selected" } func applyDeselectedStyle() { contentView.layer.borderWidth = .zero - valueStrategy = .just("Normal") + accessibilityValue = "Normal" } } diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.swift index 137e6e24e..7f1f2831f 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.swift @@ -8,7 +8,7 @@ import ReactiveDataDisplayManager import UIKit -final class HighlightableTableCell: UITableViewCell, AccessibilityInvalidatable { +final class HighlightableTableCell: UITableViewCell { // MARK: - IBOutlets @@ -17,15 +17,9 @@ final class HighlightableTableCell: UITableViewCell, AccessibilityInvalidatable // MARK: - AccessibilityInvalidatable var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } - var valueStrategy: AccessibilityStringStrategy = .just(nil) { - didSet { - accessibilityInvalidator?.invalidateParameters() - } - } + var valueStrategy: AccessibilityStringStrategy = .ignored var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } - var accessibilityInvalidator: AccessibilityItemInvalidator? - // MARK: - AccessibilityInvalidatable override func awakeFromNib() { @@ -51,23 +45,23 @@ extension HighlightableTableCell: HighlightableItem { func applyUnhighlightedStyle() { contentView.backgroundColor = .white - valueStrategy = .just("Normal") + accessibilityValue = "Normal" } func applyHighlightedStyle() { contentView.backgroundColor = .red.withAlphaComponent(0.3) - valueStrategy = .just("Highlighted") + accessibilityValue = "Highlighted" } func applySelectedStyle() { contentView.layer.borderColor = UIColor.blue.cgColor contentView.layer.borderWidth = 1 - valueStrategy = .just("Selected") + accessibilityValue = "Selected" } func applyDeselectedStyle() { contentView.layer.borderWidth = .zero - valueStrategy = .just("Normal") + accessibilityValue = "Normal" } } From 96ff3414aeb39dc4d6894929c49c49a43d8b4b81 Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Fri, 2 Jun 2023 11:43:57 +0400 Subject: [PATCH 037/126] fix test for movable plugin --- .../Plugins/MovablePluginExampleUITest.swift | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/MovablePluginExampleUITest.swift b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/MovablePluginExampleUITest.swift index e3ad6eddb..e2c7277b9 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/MovablePluginExampleUITest.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/MovablePluginExampleUITest.swift @@ -42,10 +42,16 @@ final class MovablePluginExampleUITest: BaseUITestCase { tapTableElement("Table with movable cell") let sourceCell = getCell(for: .table, collectionId: tableId, cellId: sourceDraggable) - let destinationSection = app.tables[tableId].otherElements["Section 2"] - - sourceCell.buttons.firstMatch - .press(forDuration: duration, thenDragTo: destinationSection, withVelocity: .slow, thenHoldForDuration: duration) + let destinationSection = app.tables[tableId].otherElements["Section 2"].firstMatch + + sourceCell + .firstMatch + .buttons + .firstMatch + .press(forDuration: duration, + thenDragTo: destinationSection, + withVelocity: .slow, + thenHoldForDuration: duration) sleep(Constants.waitTime) let firstCell = getFirstCell(for: .table, id: tableId) From 932daf838c83e6885f52247ff393d7ff1ce9c565 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Fri, 2 Jun 2023 13:59:39 +0300 Subject: [PATCH 038/126] Revert "revert accessibility value updates with selected/highlighted states" This reverts commit 3f0daae8892b149bb44f0288de2bdf4da7496512. --- .../TitleCollectionViewCell.swift | 16 ++++++++++------ .../HighlightableTableCell.swift | 18 ++++++++++++------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift index f2bc818e0..bd069e45f 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift @@ -9,7 +9,7 @@ import UIKit import ReactiveDataDisplayManager -final class TitleCollectionViewCell: UICollectionViewCell { +final class TitleCollectionViewCell: UICollectionViewCell, AccessibilityInvalidatable { // MARK: - IBOutlets @@ -18,7 +18,11 @@ final class TitleCollectionViewCell: UICollectionViewCell { // MARK: - AccessibilityInvalidatable var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } - var valueStrategy: AccessibilityStringStrategy = .ignored + var valueStrategy: AccessibilityStringStrategy = .just(nil) { + didSet { + accessibilityInvalidator?.invalidateParameters() + } + } var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } var accessibilityInvalidator: AccessibilityItemInvalidator? @@ -48,23 +52,23 @@ extension TitleCollectionViewCell: HighlightableItem { func applyUnhighlightedStyle() { contentView.backgroundColor = .gray - accessibilityValue = "Normal" + valueStrategy = .just("Normal") } func applyHighlightedStyle() { contentView.backgroundColor = .white.withAlphaComponent(0.5) - accessibilityValue = "Highlighted" + valueStrategy = .just("Highlighted") } func applySelectedStyle() { contentView.layer.borderColor = UIColor.blue.cgColor contentView.layer.borderWidth = 1 - accessibilityValue = "Selected" + valueStrategy = .just("Selected") } func applyDeselectedStyle() { contentView.layer.borderWidth = .zero - accessibilityValue = "Normal" + valueStrategy = .just("Normal") } } diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.swift index 7f1f2831f..137e6e24e 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.swift @@ -8,7 +8,7 @@ import ReactiveDataDisplayManager import UIKit -final class HighlightableTableCell: UITableViewCell { +final class HighlightableTableCell: UITableViewCell, AccessibilityInvalidatable { // MARK: - IBOutlets @@ -17,9 +17,15 @@ final class HighlightableTableCell: UITableViewCell { // MARK: - AccessibilityInvalidatable var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } - var valueStrategy: AccessibilityStringStrategy = .ignored + var valueStrategy: AccessibilityStringStrategy = .just(nil) { + didSet { + accessibilityInvalidator?.invalidateParameters() + } + } var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } + var accessibilityInvalidator: AccessibilityItemInvalidator? + // MARK: - AccessibilityInvalidatable override func awakeFromNib() { @@ -45,23 +51,23 @@ extension HighlightableTableCell: HighlightableItem { func applyUnhighlightedStyle() { contentView.backgroundColor = .white - accessibilityValue = "Normal" + valueStrategy = .just("Normal") } func applyHighlightedStyle() { contentView.backgroundColor = .red.withAlphaComponent(0.3) - accessibilityValue = "Highlighted" + valueStrategy = .just("Highlighted") } func applySelectedStyle() { contentView.layer.borderColor = UIColor.blue.cgColor contentView.layer.borderWidth = 1 - accessibilityValue = "Selected" + valueStrategy = .just("Selected") } func applyDeselectedStyle() { contentView.layer.borderWidth = .zero - accessibilityValue = "Normal" + valueStrategy = .just("Normal") } } From 4c33a1c1cd506d372913c3bffb3e0d1c84c81a4e Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Fri, 2 Jun 2023 15:32:46 +0300 Subject: [PATCH 039/126] added insert remove methods --- .../AccessibilityItem+Strategies.swift | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Strategies.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Strategies.swift index 3e2932dcb..df3bc1b80 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Strategies.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem+Strategies.swift @@ -44,6 +44,7 @@ public enum AccessibilityStringStrategy { return strategies.compactMap(\.value).joined() } } + } /// Accessibility strategy for `UIAccessibilityTraits` parameter @@ -83,4 +84,22 @@ public enum AccessibilityTraitsStrategy { return objects.map(\.accessibilityTraits).reduce(UIAccessibilityTraits(), { $0.union($1) }) } } + + /// Evaluates strategy and inserts provided traits + mutating public func insert(_ traits: UIAccessibilityTraits) { + guard let value else { + self = .just(traits) + return + } + self = .just(value.union(traits)) + } + + /// Evaluates strategy and removes provided traits + mutating public func remove(_ traits: UIAccessibilityTraits) { + guard let value else { + return + } + self = .just(value.subtracting(traits)) + } + } From 9685f86b62062dccdd2e381f1b987a7943a90fba Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Fri, 2 Jun 2023 15:36:33 +0300 Subject: [PATCH 040/126] fixes for ui tests --- .../Accessibility/AccessibilityItem.swift | 19 ++++++++- .../Accessibility/AccessibilityModifier.swift | 40 ++++++++++++++++++- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift index 1a50ce9ff..f38ea154c 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift @@ -18,6 +18,9 @@ public protocol AccessibilityItem: UIView, AccessibilityStrategyProvider & Acces /// Also `AccessibilityStrategyProvider` can be extended by your protocol to add new parameters. And to apply new parameters you need to provide a custom modifier var modifierType: AccessibilityModifierType { get } + /// Defines the behaviour for traits `[.selected, .notEnabled]`. By default, modifier will not override these traits + var shouldOverrideStateTraits: Bool { get } + /// Conficts resolver when generator and item contains `AccessibilityStringStrategy`. By default values will be joined with a space separator in next order: generator, item /// /// You can define your own implementation to change separator or order of values. @@ -29,7 +32,21 @@ public protocol AccessibilityItem: UIView, AccessibilityStrategyProvider & Acces } public extension AccessibilityItem { - var modifierType: AccessibilityModifierType { BaseAccessibilityModifier.self } + + var modifierType: AccessibilityModifierType { + #if DEBUG + if CommandLine.arguments.contains("-rddm.XCUITestsCompatible") { + return XCUITestsAccessibilityModifier.self + } else { + return BaseAccessibilityModifier.self + } + #else + return BaseAccessibilityModifier.self + #endif + } + + + var shouldOverrideStateTraits: Bool { false } func accessibilityStrategyConflictResolver(itemStrategy: AccessibilityStringStrategy, generatorStrategy: AccessibilityStringStrategy) -> String? { diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityModifier.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityModifier.swift index fdb90b862..741976731 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityModifier.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityModifier.swift @@ -18,6 +18,8 @@ public protocol AccessibilityModifier { /// Base modifier which handle only `AccessibilityItem` properties public enum BaseAccessibilityModifier: AccessibilityModifier { + static let stateTraits: UIAccessibilityTraits = [.selected, .notEnabled] + static public func modify(item: AccessibilityItem) { guard !item.isAccessibilityIgnored else { return @@ -33,7 +35,11 @@ public enum BaseAccessibilityModifier: AccessibilityModifier { } if !item.traitsStrategy.isIgnored, let traits = item.traitsStrategy.value { - item.accessibilityTraits = traits + if item.shouldOverrideStateTraits { + item.accessibilityTraits = traits + } else { + item.accessibilityTraits = traits.union(item.accessibilityTraits.intersection(stateTraits)) + } } item.accessibilityCustomActions = item.accessibilityActions() @@ -59,7 +65,11 @@ public enum BaseAccessibilityModifier: AccessibilityModifier { let traits = [item.traitsStrategy.value, generator.traitsStrategy.value] .compactMap { $0 } .reduce(UIAccessibilityTraits(), { $0.union($1) }) - item.accessibilityTraits = traits + if item.shouldOverrideStateTraits { + item.accessibilityTraits = traits + } else { + item.accessibilityTraits = traits.union(item.accessibilityTraits.intersection(stateTraits)) + } } item.accessibilityCustomActions = item.accessibilityActions() @@ -69,3 +79,29 @@ public enum BaseAccessibilityModifier: AccessibilityModifier { } } } + +#if DEBUG +/// Special modifier for UI tests, which adds `.button` trait for `UITableViewCell` and `UICollectionViewCell` to make them conform to cell `XCUIElement` type. +/// Also adds trait `.staticText` for headers and footers to make them accessible by identifier +public enum XCUITestsAccessibilityModifier: AccessibilityModifier { + + static public func modify(item: AccessibilityItem) { + BaseAccessibilityModifier.modify(item: item) + if item is UITableViewCell || item is UICollectionViewCell { + item.accessibilityTraits.insert(.button) + } else { + item.accessibilityTraits.insert(.staticText) + } + } + + static public func modify(item: AccessibilityItem, generator: AccessibilityStrategyProvider) { + BaseAccessibilityModifier.modify(item: item, generator: generator) + if item is UITableViewCell || item is UICollectionViewCell { + item.accessibilityTraits.insert(.button) + } else { + item.accessibilityTraits.insert(.staticText) + } + } + +} +#endif From f8361a48a463c3c20036f97fc4589ff6ee327ba3 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Fri, 2 Jun 2023 15:36:53 +0300 Subject: [PATCH 041/126] fixed ui tests --- .../TitleCollectionViewCell.swift | 23 ++++++----- .../AccessibilityItem+CommandLine.swift | 41 ------------------- .../HighlightableTableCell.swift | 24 ++++++----- .../BaseClasses/BaseUITestCase.swift | 2 +- 4 files changed, 26 insertions(+), 64 deletions(-) delete mode 100644 Example/ReactiveDataDisplayManager/Extensions/AccessibilityItem+CommandLine.swift diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift index bd069e45f..3c9c5d192 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.swift @@ -18,12 +18,9 @@ final class TitleCollectionViewCell: UICollectionViewCell, AccessibilityInvalida // MARK: - AccessibilityInvalidatable var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } - var valueStrategy: AccessibilityStringStrategy = .just(nil) { - didSet { - accessibilityInvalidator?.invalidateParameters() - } - } - var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } + var valueStrategy: AccessibilityStringStrategy = .just(nil) + lazy var traitsStrategy: AccessibilityTraitsStrategy = .from(object: titleLabel) + var shouldOverrideStateTraits: Bool { true } var accessibilityInvalidator: AccessibilityItemInvalidator? @@ -52,23 +49,23 @@ extension TitleCollectionViewCell: HighlightableItem { func applyUnhighlightedStyle() { contentView.backgroundColor = .gray - valueStrategy = .just("Normal") + updateState(state: "Normal", isSelected: false) } func applyHighlightedStyle() { contentView.backgroundColor = .white.withAlphaComponent(0.5) - valueStrategy = .just("Highlighted") + updateState(state: "Highlighted", isSelected: false) } func applySelectedStyle() { contentView.layer.borderColor = UIColor.blue.cgColor contentView.layer.borderWidth = 1 - valueStrategy = .just("Selected") + updateState(state: "Selected", isSelected: true) } func applyDeselectedStyle() { contentView.layer.borderWidth = .zero - valueStrategy = .just("Normal") + updateState(state: "Normal", isSelected: false) } } @@ -76,11 +73,15 @@ extension TitleCollectionViewCell: HighlightableItem { // MARK: - Private private extension TitleCollectionViewCell { - func configureAppearance() { contentView.backgroundColor = .gray contentView.layer.cornerRadius = 20 contentView.clipsToBounds = true } + func updateState(state: String, isSelected: Bool) { + valueStrategy = .just(state) + isSelected ? traitsStrategy.insert(.selected) : traitsStrategy.remove(.selected) + accessibilityInvalidator?.invalidateParameters() + } } diff --git a/Example/ReactiveDataDisplayManager/Extensions/AccessibilityItem+CommandLine.swift b/Example/ReactiveDataDisplayManager/Extensions/AccessibilityItem+CommandLine.swift deleted file mode 100644 index 1d80b2118..000000000 --- a/Example/ReactiveDataDisplayManager/Extensions/AccessibilityItem+CommandLine.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// AccessibilityItem+CommandLine.swift -// ReactiveDataDisplayManagerExample_iOS -// -// Created by Никита Коробейников on 02.06.2023. -// - -import Foundation -import ReactiveDataDisplayManager - -extension AccessibilityItem { - var modifierType: AccessibilityModifierType { - #if DEBUG - if CommandLine.arguments.contains("-doNotOverrideTraits") { - return NoTraitsAccessibilityModifier.self - } else { - return BaseAccessibilityModifier.self - } - #else - return BaseAccessibilityModifier.self - #endif - } -} - -// MARK: - NoTraitsAccessibilityModifier - -enum NoTraitsAccessibilityModifier: AccessibilityModifier { - - static func modify(item: AccessibilityItem) { - let traits = item.accessibilityTraits - BaseAccessibilityModifier.self.modify(item: item) - item.accessibilityTraits = traits - } - - static func modify(item: AccessibilityItem, generator: AccessibilityStrategyProvider) { - let traits = item.accessibilityTraits - BaseAccessibilityModifier.self.modify(item: item, generator: generator) - item.accessibilityTraits = traits - } - -} diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.swift index 137e6e24e..f44af2797 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.swift @@ -17,12 +17,10 @@ final class HighlightableTableCell: UITableViewCell, AccessibilityInvalidatable // MARK: - AccessibilityInvalidatable var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } - var valueStrategy: AccessibilityStringStrategy = .just(nil) { - didSet { - accessibilityInvalidator?.invalidateParameters() - } - } - var traitsStrategy: AccessibilityTraitsStrategy { .from(object: titleLabel) } + var valueStrategy: AccessibilityStringStrategy = .just(nil) + lazy var traitsStrategy: AccessibilityTraitsStrategy = .from(object: titleLabel) + + var shouldOverrideStateTraits: Bool { true } var accessibilityInvalidator: AccessibilityItemInvalidator? @@ -51,23 +49,23 @@ extension HighlightableTableCell: HighlightableItem { func applyUnhighlightedStyle() { contentView.backgroundColor = .white - valueStrategy = .just("Normal") + updateState(state: "Normal", isSelected: false) } func applyHighlightedStyle() { contentView.backgroundColor = .red.withAlphaComponent(0.3) - valueStrategy = .just("Highlighted") + updateState(state: "Highlighted", isSelected: false) } func applySelectedStyle() { contentView.layer.borderColor = UIColor.blue.cgColor contentView.layer.borderWidth = 1 - valueStrategy = .just("Selected") + updateState(state: "Selected", isSelected: true) } func applyDeselectedStyle() { contentView.layer.borderWidth = .zero - valueStrategy = .just("Normal") + updateState(state: "Normal", isSelected: false) } } @@ -75,10 +73,14 @@ extension HighlightableTableCell: HighlightableItem { // MARK: - Private private extension HighlightableTableCell { - func configureAppearance() { selectionStyle = .none contentView.backgroundColor = .white } + func updateState(state: String, isSelected: Bool) { + valueStrategy = .just(state) + isSelected ? traitsStrategy.insert(.selected) : traitsStrategy.remove(.selected) + accessibilityInvalidator?.invalidateParameters() + } } diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/BaseClasses/BaseUITestCase.swift b/Example/ReactiveDataDisplayManagerExampleUITests/BaseClasses/BaseUITestCase.swift index 5509f2a17..0440ac226 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/BaseClasses/BaseUITestCase.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/BaseClasses/BaseUITestCase.swift @@ -23,7 +23,7 @@ open class BaseUITestCase: XCTestCase { continueAfterFailure = false app = XCUIApplication() app.launchArguments.append("-disableAnimations") - app.launchArguments.append("-doNotOverrideTraits") + app.launchArguments.append("-rddm.XCUITestsCompatible") app.launchArguments.append(contentsOf: additionalCommands) app.launch() } From b1215a5da54d74427acb9c30512a1e63ea7b77a0 Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Thu, 1 Jun 2023 20:50:56 +0400 Subject: [PATCH 042/126] add footers for table --- .../EmptyCollectionFooterGenarator.swift | 11 +++ .../EmptyCollectionHeaderGenerator.swift | 2 +- .../Protocols/FooterDataDisplayManager.swift | 2 +- Source/Protocols/GeneratorsProvider.swift | 2 + Source/Protocols/Protocols.swift | 26 ++++++ .../DataSource/TableGeneratorsProvider.swift | 1 + .../Generators/EmptyFooterGenerator.swift | 32 +++++++ .../EmptyGravityFooterGenerator.swift | 24 +++++ .../Generators/EmptyHeaderGenerator.swift | 2 +- Source/Table/Manager/BaseTableManager.swift | 89 ++++++++++++++++--- .../Table/Manager/GravityTableManager.swift | 4 - Source/Table/Manager/ManualTableManager.swift | 39 +------- 12 files changed, 178 insertions(+), 56 deletions(-) create mode 100644 Source/Table/Generators/EmptyFooterGenerator.swift create mode 100644 Source/Table/Generators/EmptyGravityFooterGenerator.swift diff --git a/Source/Collection/Generators/EmptyCollectionFooterGenarator.swift b/Source/Collection/Generators/EmptyCollectionFooterGenarator.swift index 195bd0129..1697378f1 100644 --- a/Source/Collection/Generators/EmptyCollectionFooterGenarator.swift +++ b/Source/Collection/Generators/EmptyCollectionFooterGenarator.swift @@ -9,6 +9,7 @@ import UIKit public class EmptyCollectionFooterGenerator: CollectionFooterGenerator { + public let uuid = UUID().uuidString public let elementKind = UICollectionView.elementKindSectionFooter public func size(_ collectionView: UICollectionView, forSection section: Int) -> CGSize { @@ -32,3 +33,13 @@ public class EmptyCollectionFooterGenerator: CollectionFooterGenerator { return UICollectionReusableView.self } } + +// MARK: - DiffableItemSource + +extension EmptyCollectionFooterGenerator: DiffableItemSource { + + public var diffableItem: DiffableItem { + DiffableItem(id: uuid, state: .init("RDDM.Diffable.EmptySection.footer")) + } + +} diff --git a/Source/Collection/Generators/EmptyCollectionHeaderGenerator.swift b/Source/Collection/Generators/EmptyCollectionHeaderGenerator.swift index f5863ca52..e64b4657b 100644 --- a/Source/Collection/Generators/EmptyCollectionHeaderGenerator.swift +++ b/Source/Collection/Generators/EmptyCollectionHeaderGenerator.swift @@ -42,7 +42,7 @@ public class EmptyCollectionHeaderGenerator: CollectionHeaderGenerator { extension EmptyCollectionHeaderGenerator: DiffableItemSource { public var diffableItem: DiffableItem { - DiffableItem(id: uuid, state: .init("RDDM.Diffable.EmptySection")) + DiffableItem(id: uuid, state: .init("RDDM.Diffable.EmptySection.header")) } } diff --git a/Source/Protocols/FooterDataDisplayManager.swift b/Source/Protocols/FooterDataDisplayManager.swift index 64602e720..b8b609fac 100644 --- a/Source/Protocols/FooterDataDisplayManager.swift +++ b/Source/Protocols/FooterDataDisplayManager.swift @@ -27,7 +27,7 @@ public protocol FooterDataDisplayManager: AnyObject { func addCellGenerators(_ generators: [CellGeneratorType], toFooter footer: FooterGeneratorType) - func removeAllGenerators(from header: FooterGeneratorType) + func removeAllGenerators(from footer: FooterGeneratorType) /// Removes all header generators. func clearFooterGenerators() diff --git a/Source/Protocols/GeneratorsProvider.swift b/Source/Protocols/GeneratorsProvider.swift index 984cdb9fd..3db483031 100644 --- a/Source/Protocols/GeneratorsProvider.swift +++ b/Source/Protocols/GeneratorsProvider.swift @@ -10,7 +10,9 @@ import Foundation public protocol GeneratorsProvider: AnyObject { associatedtype CellGeneratorType associatedtype HeaderGeneratorType + associatedtype FooterGeneratorType var generators: [[CellGeneratorType]] { get set } var sections: [HeaderGeneratorType] { get set } + var footers: [FooterGeneratorType] { get set } } diff --git a/Source/Protocols/Protocols.swift b/Source/Protocols/Protocols.swift index d0fc4baba..65447a7cb 100644 --- a/Source/Protocols/Protocols.swift +++ b/Source/Protocols/Protocols.swift @@ -27,6 +27,24 @@ open class TableHeaderGenerator: ViewGenerator, AccessibilityStrategyProvider { } } +// sourcery: AutoMockable +open class TableFooterGenerator: ViewGenerator, AccessibilityStrategyProvider { + + public let uuid = UUID().uuidString + + open var traitsStrategy: AccessibilityTraitsStrategy { .ignored } + + public init() { } + + open func generate() -> UIView { + preconditionFailure("\(#function) must be overriden in child") + } + + open func height(_ tableView: UITableView, forSection section: Int) -> CGFloat { + preconditionFailure("\(#function) must be overriden in child") + } +} + // sourcery: AutoMockable /// Protocol that incapsulated type of current cell public protocol TableCellGenerator: AnyObject { @@ -326,6 +344,14 @@ open class GravityTableHeaderGenerator: TableHeaderGenerator, GravityItem { } } +open class GravityTableFooterGenerator: TableFooterGenerator, GravityItem { + open var heaviness = 0 + + open func getHeaviness() -> Int { + preconditionFailure("\(#function) must be overriden in child") + } +} + // MARK: - Equatable extension GravityTableHeaderGenerator: Equatable { diff --git a/Source/Table/DataSource/TableGeneratorsProvider.swift b/Source/Table/DataSource/TableGeneratorsProvider.swift index 114857346..769e73b42 100644 --- a/Source/Table/DataSource/TableGeneratorsProvider.swift +++ b/Source/Table/DataSource/TableGeneratorsProvider.swift @@ -11,4 +11,5 @@ open class TableGeneratorsProvider: GeneratorsProvider { open var sections = [TableHeaderGenerator]() open var generators = [[TableCellGenerator]]() + open var footers = [TableFooterGenerator]() } diff --git a/Source/Table/Generators/EmptyFooterGenerator.swift b/Source/Table/Generators/EmptyFooterGenerator.swift new file mode 100644 index 000000000..8cc8131db --- /dev/null +++ b/Source/Table/Generators/EmptyFooterGenerator.swift @@ -0,0 +1,32 @@ +// +// EmptyTableFooterGenerator.swift +// ReactiveDataDisplayManager +// +// Created by Никита Коробейников on 01.06.2023. +// + +import UIKit + +public class EmptyTableFooterGenerator: TableFooterGenerator { + + open override func generate() -> UIView { + let view = UIView() + view.backgroundColor = UIColor.clear + return view + } + + open override func height(_ tableView: UITableView, forSection section: Int) -> CGFloat { + return 0.01 + } + +} + +// MARK: - DiffableItemSource + +extension EmptyTableFooterGenerator: DiffableItemSource { + + public var diffableItem: DiffableItem { + DiffableItem(id: uuid, state: .init("RDDM.Diffable.EmptySection.footer")) + } + +} diff --git a/Source/Table/Generators/EmptyGravityFooterGenerator.swift b/Source/Table/Generators/EmptyGravityFooterGenerator.swift new file mode 100644 index 000000000..e5e4d1b3f --- /dev/null +++ b/Source/Table/Generators/EmptyGravityFooterGenerator.swift @@ -0,0 +1,24 @@ +// +// EmptyGravityFooterGenerator.swift +// ReactiveDataDisplayManager +// +// Created by Никита Коробейников on 01.06.2023. +// + +import UIKit + +public class EmptyGravityFooterGenerator: GravityTableFooterGenerator { + open override func generate() -> UIView { + let view = UIView() + view.backgroundColor = UIColor.clear + return view + } + + open override func height(_ tableView: UITableView, forSection section: Int) -> CGFloat { + return 0.01 + } + + open override func getHeaviness() -> Int { + return 0 + } +} diff --git a/Source/Table/Generators/EmptyHeaderGenerator.swift b/Source/Table/Generators/EmptyHeaderGenerator.swift index d57b92b44..0573f2122 100644 --- a/Source/Table/Generators/EmptyHeaderGenerator.swift +++ b/Source/Table/Generators/EmptyHeaderGenerator.swift @@ -27,7 +27,7 @@ public class EmptyTableHeaderGenerator: TableHeaderGenerator { extension EmptyTableHeaderGenerator: DiffableItemSource { public var diffableItem: DiffableItem { - DiffableItem(id: uuid, state: .init("RDDM.Diffable.EmptySection")) + DiffableItem(id: uuid, state: .init("RDDM.Diffable.EmptySection.header")) } } diff --git a/Source/Table/Manager/BaseTableManager.swift b/Source/Table/Manager/BaseTableManager.swift index 8d6633254..2ac92bf02 100644 --- a/Source/Table/Manager/BaseTableManager.swift +++ b/Source/Table/Manager/BaseTableManager.swift @@ -16,6 +16,7 @@ open class BaseTableManager: TableGeneratorsProvider, DataDisplayManager { public typealias CollectionType = UITableView public typealias CellGeneratorType = TableCellGenerator public typealias HeaderGeneratorType = TableHeaderGenerator + public typealias FooterGeneratorType = TableFooterGenerator public typealias TableModifier = Modifier // MARK: - Public properties @@ -46,6 +47,9 @@ open class BaseTableManager: TableGeneratorsProvider, DataDisplayManager { if sections.count <= 0 { sections.append(EmptyTableHeaderGenerator()) } + if footers.count <= 0 { + footers.append(EmptyTableFooterGenerator()) + } // Add to last section let index = sections.count - 1 self.generators[index < 0 ? 0 : index].append(generator) @@ -99,18 +103,81 @@ open class BaseTableManager: TableGeneratorsProvider, DataDisplayManager { needRemoveEmptySection: needRemoveEmptySection) } - // MARK: - Deprecated + // MARK: - HeaderDataDisplayManager - @available(*, deprecated, message: "Please use method with a new `TableRowAnimation` parameter") - open func remove(_ generator: TableCellGenerator, - with animation: UITableView.RowAnimation?, - needScrollAt scrollPosition: UITableView.ScrollPosition?, - needRemoveEmptySection: Bool) { - guard let index = findGenerator(generator) else { return } - self.removeGenerator(with: index, - with: animation.map { .animated($0) } ?? .notAnimated, - needScrollAt: scrollPosition, - needRemoveEmptySection: needRemoveEmptySection) + open func addSectionHeaderGenerator(_ generator: TableHeaderGenerator) { + self.sections.append(generator) + if sections.count > generators.count { + self.generators.append([]) + } + } + + open func addCellGenerator(_ generator: CellGeneratorType, toHeader header: TableHeaderGenerator) { + addCellGenerators([generator], toHeader: header) + } + + open func addCellGenerators(_ generators: [CellGeneratorType], toHeader header: TableHeaderGenerator) { + generators.forEach { $0.registerCell(in: view) } + + if self.generators.count != self.sections.count || sections.isEmpty { + self.generators.append([TableCellGenerator]()) + } + + if let index = self.sections.firstIndex(where: { $0 === header }) { + self.generators[index].append(contentsOf: generators) + } + } + + open func removeAllGenerators(from header: TableHeaderGenerator) { + guard + let index = self.sections.firstIndex(where: { $0 === header }), + self.generators.count > index + else { + return + } + + self.generators[index].removeAll() + } + + open func clearHeaderGenerators() { + self.sections.removeAll() + } + + // MARK: - FooterDataDisplayManager + + open func addSectionFooterGenerator(_ generator: TableFooterGenerator) { + self.footers.append(generator) + } + + open func addCellGenerator(_ generator: CellGeneratorType, toFooter footer: TableFooterGenerator) { + addCellGenerators([generator], toFooter: footer) + } + + open func addCellGenerators(_ generators: [CellGeneratorType], toFooter footer: TableFooterGenerator) { + generators.forEach { $0.registerCell(in: view) } + + if self.generators.count != self.footers.count || footers.isEmpty { + self.generators.append([TableCellGenerator]()) + } + + if let index = self.footers.firstIndex(where: { $0 === footer }) { + self.generators[index].append(contentsOf: generators) + } + } + + open func removeAllGenerators(from footer: TableFooterGenerator) { + guard + let index = self.footers.firstIndex(where: { $0 === footer }), + self.generators.count > index + else { + return + } + + self.generators[index].removeAll() + } + + open func clearFooterGenerators() { + self.footers.removeAll() } } diff --git a/Source/Table/Manager/GravityTableManager.swift b/Source/Table/Manager/GravityTableManager.swift index 7f2c0fb6f..d602b0aa7 100644 --- a/Source/Table/Manager/GravityTableManager.swift +++ b/Source/Table/Manager/GravityTableManager.swift @@ -137,10 +137,6 @@ open class GravityTableManager: BaseTableManager { self.generators[index].removeAll() } - public func clearHeaderGenerators() { - sections.removeAll() - } - open func replace(oldGenerator: CellGeneratorType, on newGenerator: CellGeneratorType, removeInsertAnimation: TableRowAnimationGroup = .animated(.automatic, .automatic)) { diff --git a/Source/Table/Manager/ManualTableManager.swift b/Source/Table/Manager/ManualTableManager.swift index 3928e5b91..9c91deb28 100644 --- a/Source/Table/Manager/ManualTableManager.swift +++ b/Source/Table/Manager/ManualTableManager.swift @@ -24,17 +24,6 @@ public class ManualTableManager: BaseTableManager { self.generators.append(cells) } - /// Adds section TableHeaderGenerator generator to the end of collection - /// - /// - Parameters: - /// - generator: Generator for new section TableHeaderGenerator. - open func addSectionHeaderGenerator(_ generator: TableHeaderGenerator) { - self.sections.append(generator) - if sections.count > generators.count { - self.generators.append([]) - } - } - /// Inserts new TableHeaderGenerator generator after another. /// /// - Parameters: @@ -55,11 +44,6 @@ public class ManualTableManager: BaseTableManager { self.insert(headGenerator: headGenerator, by: newIndex, animation: .notAnimated) } - /// Removes all headers generators - open func clearHeaderGenerators() { - self.sections.removeAll() - } - /// Reloads only one section with specified animation /// /// - Parameters: @@ -72,25 +56,8 @@ public class ManualTableManager: BaseTableManager { modifier?.reloadSections(at: [index], with: animation) } - /// Inserts new generators to provided TableHeaderGenerator generator. - /// - /// - Parameters: - /// - generators: Generators to insert - /// - TableHeaderGenerator: TableHeaderGenerator generator in which you want to insert. - open func addCellGenerators(_ generators: [TableCellGenerator], toHeader headerGenerator: TableHeaderGenerator) { - generators.forEach { $0.registerCell(in: view) } - - if self.generators.count != self.sections.count || sections.isEmpty { - self.generators.append([TableCellGenerator]()) - } - - if let index = self.sections.firstIndex(where: { $0 === headerGenerator }) { - self.generators[index].append(contentsOf: generators) - } - } - /// Removes all TableCellGenerator generators from a given section - open func removeAllGenerators(from headerGenerator: TableHeaderGenerator) { + open override func removeAllGenerators(from headerGenerator: TableHeaderGenerator) { guard let index = self.sections.firstIndex(where: { $0 === headerGenerator }), self.generators.count > index else { return } @@ -108,10 +75,6 @@ public class ManualTableManager: BaseTableManager { modifier?.removeRows(at: indexes, and: nil, with: .automatic) } - open func addCellGenerator(_ generator: TableCellGenerator, toHeader headerGenerator: TableHeaderGenerator) { - addCellGenerators([generator], toHeader: headerGenerator) - } - /// Inserts new head generator. /// /// - Parameters: From 6b50fd8ec42dbc5b1d160f2fda484fce72bf072d Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Thu, 1 Jun 2023 20:58:05 +0400 Subject: [PATCH 043/126] fix Unit and UI tests --- .../Plugins/SwipeActionsPluginExampleUITest.swift | 8 ++++---- Source/Collection/Manager/BaseCollectionManager.swift | 6 +----- Source/Stack/Manager/BaseStackManager.swift | 4 ++-- Source/Table/Manager/BaseTableManager.swift | 2 +- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/SwipeActionsPluginExampleUITest.swift b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/SwipeActionsPluginExampleUITest.swift index c906f6ac1..d5ce5b12b 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/SwipeActionsPluginExampleUITest.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/SwipeActionsPluginExampleUITest.swift @@ -19,12 +19,12 @@ final class SwipeActionsPluginExampleUITest: BaseUITestCase { cell.swipeLeft() XCTAssertTrue(collectionView.buttons["Flag"].exists) XCTAssertTrue(collectionView.buttons["More"].exists) - XCTAssertTrue(collectionView.buttons.count == 3) +// XCTAssertTrue(collectionView.buttons.count == 3) cell.swipeRight() XCTAssertFalse(collectionView.buttons["Flag"].exists) XCTAssertFalse(collectionView.buttons["More"].exists) - XCTAssertTrue(collectionView.buttons.count == 0) +// XCTAssertTrue(collectionView.buttons.count == 0) } func testCollection_whenSwipeRightFirstCell_thenCellHaveThreeButtons() throws { @@ -38,13 +38,13 @@ final class SwipeActionsPluginExampleUITest: BaseUITestCase { XCTAssertTrue(collectionView.buttons["Delete"].exists) XCTAssertTrue(collectionView.buttons["Info"].exists) XCTAssertTrue(collectionView.buttons["Apply"].exists) - XCTAssertTrue(collectionView.buttons.count == 3) +// XCTAssertTrue(collectionView.buttons.count == 3) cell.swipeLeft() XCTAssertFalse(collectionView.buttons["Delete"].exists) XCTAssertFalse(collectionView.buttons["Info"].exists) XCTAssertFalse(collectionView.buttons["Apply"].exists) - XCTAssertTrue(collectionView.buttons.count == 0) +// XCTAssertTrue(collectionView.buttons.count == 0) } func testTable_whenSwipeLeftFirstCell_thenCellHaveThreeButtons() throws { diff --git a/Source/Collection/Manager/BaseCollectionManager.swift b/Source/Collection/Manager/BaseCollectionManager.swift index f37a9910b..dd59b6c63 100644 --- a/Source/Collection/Manager/BaseCollectionManager.swift +++ b/Source/Collection/Manager/BaseCollectionManager.swift @@ -220,11 +220,7 @@ private extension BaseCollectionManager { elements.forEach { element in element.generator.registerCell(in: view) - if self.generators.count == element.sectionIndex { - self.generators.append([element.generator]) - } else { - self.generators[element.sectionIndex].insert(element.generator, at: element.generatorIndex) - } + generators[element.sectionIndex].insert(element.generator, at: element.generatorIndex) } let indexPaths = elements.map { IndexPath(item: $0.generatorIndex, section: $0.sectionIndex) } diff --git a/Source/Stack/Manager/BaseStackManager.swift b/Source/Stack/Manager/BaseStackManager.swift index 10cfdc355..0758fb78d 100644 --- a/Source/Stack/Manager/BaseStackManager.swift +++ b/Source/Stack/Manager/BaseStackManager.swift @@ -28,8 +28,8 @@ open class BaseStackManager: DataDisplayManager { public func forceRefill() { view.arrangedSubviews.forEach { $0.removeFromSuperview() } - cellGenerators.enumerated().forEach { [weak self] offset, generator in - guard let stackView = self?.view else { return } + cellGenerators.enumerated().forEach { offset, generator in + guard let stackView = self.view else { return } let view = generator.generate(stackView: stackView, index: offset) stackView.addArrangedSubview(view) } diff --git a/Source/Table/Manager/BaseTableManager.swift b/Source/Table/Manager/BaseTableManager.swift index 2ac92bf02..fc7ed4b95 100644 --- a/Source/Table/Manager/BaseTableManager.swift +++ b/Source/Table/Manager/BaseTableManager.swift @@ -259,7 +259,7 @@ extension BaseTableManager { elements.forEach { element in element.generator.registerCell(in: view) - self.generators[element.sectionIndex].insert(element.generator, at: element.generatorIndex) + generators[element.sectionIndex].insert(element.generator, at: element.generatorIndex) } let indexPaths = elements.map { IndexPath(row: $0.generatorIndex, section: $0.sectionIndex) } From 46e61718016f27a6c69339c151242b3f3fc73f14 Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Thu, 1 Jun 2023 21:08:06 +0400 Subject: [PATCH 044/126] add generator to demonstrate table footer --- .../SectionTitleTableViewController.swift | 4 ++ .../SectionTitleFooterGenerator.swift | 48 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 Example/ReactiveDataDisplayManager/Table/Views/Headers/Generators/SectionTitleFooterGenerator.swift diff --git a/Example/ReactiveDataDisplayManager/Table/SectionTitleTableViewController/SectionTitleTableViewController.swift b/Example/ReactiveDataDisplayManager/Table/SectionTitleTableViewController/SectionTitleTableViewController.swift index 9197ed98b..8698b7567 100644 --- a/Example/ReactiveDataDisplayManager/Table/SectionTitleTableViewController/SectionTitleTableViewController.swift +++ b/Example/ReactiveDataDisplayManager/Table/SectionTitleTableViewController/SectionTitleTableViewController.swift @@ -63,6 +63,10 @@ private extension SectionTitleTableViewController { let headerGenerator = SectionTitleHeaderGenerator(model: $0.title, needSectionIndexTitle: $0.needDisplaySectionTitle) // Add header generator into adapter adapter.addSectionHeaderGenerator(headerGenerator) + // Create footer generator + let footerGenerator = SectionTitleFooterGenerator(model: $0.title) + // Add footer generator into adapter + adapter.addSectionFooterGenerator(footerGenerator) // Add cell generators into adapter adapter.addCellGenerators(makeCellGenerators()) } diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Headers/Generators/SectionTitleFooterGenerator.swift b/Example/ReactiveDataDisplayManager/Table/Views/Headers/Generators/SectionTitleFooterGenerator.swift new file mode 100644 index 000000000..2bf2e8759 --- /dev/null +++ b/Example/ReactiveDataDisplayManager/Table/Views/Headers/Generators/SectionTitleFooterGenerator.swift @@ -0,0 +1,48 @@ +// +// SectionTitleFooterGenerator.swift +// ReactiveDataDisplayManagerExample_iOS +// +// Created by Никита Коробейников on 01.06.2023. +// + +import UIKit +import ReactiveDataDisplayManager + +final class SectionTitleFooterGenerator: TableFooterGenerator { + + // MARK: - Constants + + private enum Constants { + static let defaultHeight: CGFloat = 24 + } + + // MARK: - Events + + var willDisplayEvent = BaseEvent() + var didEndDisplayEvent = BaseEvent() + var didEndDisplayCellEvent: BaseEvent? + + // MARK: - Private Property + + private let model: String + private lazy var view = HeaderView().fromNib() + + // MARK: - Initialization + + init(model: String) { + self.model = model + } + + // MARK: - TableHeaderGenerator + + override func generate() -> UIView { + guard let view = view else { return UIView() } + view.configure(with: model) + return view + } + + override func height(_ tableView: UITableView, forSection section: Int) -> CGFloat { + view?.frame.height ?? Constants.defaultHeight + } + +} From 7581c229667738ba4dfd897307e3eeaa0530ceff Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Thu, 1 Jun 2023 21:11:21 +0400 Subject: [PATCH 045/126] add required methods to table delegate --- Source/Table/Delegate/BaseTableDelegate.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Source/Table/Delegate/BaseTableDelegate.swift b/Source/Table/Delegate/BaseTableDelegate.swift index 89913909f..3cacdae44 100644 --- a/Source/Table/Delegate/BaseTableDelegate.swift +++ b/Source/Table/Delegate/BaseTableDelegate.swift @@ -153,6 +153,20 @@ extension BaseTableDelegate { return manager.sections[section].height(tableView, forSection: section) } + open func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + guard let manager = manager, section <= manager.footers.count - 1 else { + return nil + } + return manager.footers[section].generate() + } + + open func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + guard let manager = manager, section <= manager.footers.count - 1 else { + return 0.1 + } + return manager.footers[section].height(tableView, forSection: section) + } + open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tablePlugins.process(event: .didSelect(indexPath), with: manager) } From 3e2d07e188da3d2b33368e9874e88530dfde3261 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Fri, 2 Jun 2023 15:44:45 +0300 Subject: [PATCH 046/126] fixed footer --- Source/Protocols/Protocols.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Protocols/Protocols.swift b/Source/Protocols/Protocols.swift index 65447a7cb..d362893a0 100644 --- a/Source/Protocols/Protocols.swift +++ b/Source/Protocols/Protocols.swift @@ -32,7 +32,8 @@ open class TableFooterGenerator: ViewGenerator, AccessibilityStrategyProvider { public let uuid = UUID().uuidString - open var traitsStrategy: AccessibilityTraitsStrategy { .ignored } + open var labelStrategy: AccessibilityStringStrategy = .ignored + open var traitsStrategy: AccessibilityTraitsStrategy = .ignored public init() { } From 6e706d81e5aae2e278e8692537707a4311789f3a Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Fri, 2 Jun 2023 15:47:46 +0300 Subject: [PATCH 047/126] fixed ui tests from target branch --- .../Plugins/SwipeActionsPluginExampleUITest.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/SwipeActionsPluginExampleUITest.swift b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/SwipeActionsPluginExampleUITest.swift index d5ce5b12b..c906f6ac1 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/SwipeActionsPluginExampleUITest.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/SwipeActionsPluginExampleUITest.swift @@ -19,12 +19,12 @@ final class SwipeActionsPluginExampleUITest: BaseUITestCase { cell.swipeLeft() XCTAssertTrue(collectionView.buttons["Flag"].exists) XCTAssertTrue(collectionView.buttons["More"].exists) -// XCTAssertTrue(collectionView.buttons.count == 3) + XCTAssertTrue(collectionView.buttons.count == 3) cell.swipeRight() XCTAssertFalse(collectionView.buttons["Flag"].exists) XCTAssertFalse(collectionView.buttons["More"].exists) -// XCTAssertTrue(collectionView.buttons.count == 0) + XCTAssertTrue(collectionView.buttons.count == 0) } func testCollection_whenSwipeRightFirstCell_thenCellHaveThreeButtons() throws { @@ -38,13 +38,13 @@ final class SwipeActionsPluginExampleUITest: BaseUITestCase { XCTAssertTrue(collectionView.buttons["Delete"].exists) XCTAssertTrue(collectionView.buttons["Info"].exists) XCTAssertTrue(collectionView.buttons["Apply"].exists) -// XCTAssertTrue(collectionView.buttons.count == 3) + XCTAssertTrue(collectionView.buttons.count == 3) cell.swipeLeft() XCTAssertFalse(collectionView.buttons["Delete"].exists) XCTAssertFalse(collectionView.buttons["Info"].exists) XCTAssertFalse(collectionView.buttons["Apply"].exists) -// XCTAssertTrue(collectionView.buttons.count == 0) + XCTAssertTrue(collectionView.buttons.count == 0) } func testTable_whenSwipeLeftFirstCell_thenCellHaveThreeButtons() throws { From 0bfb1d4984ebcf64f1a5cbb98cdd645f9db25d09 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Fri, 2 Jun 2023 15:55:10 +0300 Subject: [PATCH 048/126] fixed swiftlint issues --- .../FoldableCollectionViewCell.swift | 2 +- .../FoldableTableViewCell/FoldableTableViewCell.swift | 2 +- .../Cells/ImageTableViewCell/ImageTableViewCell.swift | 3 +-- .../TitleWithIconTableViewCell.swift | 1 - Source/Collection/Events/CollectionEvent.swift | 1 + .../Generators/Accessibility/AccessibilityItem.swift | 3 +-- .../Accessibility/AccessibilityModifier.swift | 10 +++++----- Source/Table/Events/TableEvent.swift | 1 + 8 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FoldableCollectionViewCell/FoldableCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FoldableCollectionViewCell/FoldableCollectionViewCell.swift index 95a19a28d..4f161a69c 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FoldableCollectionViewCell/FoldableCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FoldableCollectionViewCell/FoldableCollectionViewCell.swift @@ -62,7 +62,7 @@ extension FoldableCollectionViewCell: AccessibilityItem { var labelStrategy: AccessibilityStringStrategy { .just("Expandable item") } var traitsStrategy: AccessibilityTraitsStrategy { .just(.none) } - + } // MARK: - Configuration diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/FoldableTableViewCell/FoldableTableViewCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/FoldableTableViewCell/FoldableTableViewCell.swift index d4b4dce17..d83e337a1 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/FoldableTableViewCell/FoldableTableViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/FoldableTableViewCell/FoldableTableViewCell.swift @@ -30,7 +30,7 @@ final class FoldableTableViewCell: UITableViewCell, FoldableStateHolder, Accessi // MARK: - Private Properties - private var isExpanded: Bool = false { + private var isExpanded = false { didSet { accessibilityInvalidator?.invalidateParameters() } diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.swift index b6dafcc09..5e5984d2c 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.swift @@ -53,9 +53,8 @@ extension ImageTableViewCell: AccessibilityItem { var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) } var traitsStrategy: AccessibilityTraitsStrategy { .merge([iconView, titleLabel]) } - -} +} // MARK: - ConfigurableItem diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleWithIconTableViewCell/TitleWithIconTableViewCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleWithIconTableViewCell/TitleWithIconTableViewCell.swift index 631231b78..8757f1f51 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleWithIconTableViewCell/TitleWithIconTableViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleWithIconTableViewCell/TitleWithIconTableViewCell.swift @@ -32,7 +32,6 @@ extension TitleWithIconTableViewCell: AccessibilityItem { } - // MARK: - ConfigurableItem extension TitleWithIconTableViewCell: ConfigurableItem { diff --git a/Source/Collection/Events/CollectionEvent.swift b/Source/Collection/Events/CollectionEvent.swift index 2fbe5698a..727d2453a 100644 --- a/Source/Collection/Events/CollectionEvent.swift +++ b/Source/Collection/Events/CollectionEvent.swift @@ -20,6 +20,7 @@ public enum CollectionEvent { case move(from: IndexPath, to: IndexPath) // MARK: - Accessibility Events + case invalidatedCellAccessibility(IndexPath, UICollectionViewCell) case invalidatedHeaderAccessibility(Int, UICollectionReusableView) case invalidatedFooterAccessibility(Int, UICollectionReusableView) diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift index f38ea154c..dbe8c8932 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift @@ -9,7 +9,7 @@ import UIKit /// Protocol for cells to adopt accesibility public protocol AccessibilityItem: UIView, AccessibilityStrategyProvider & AccessibilityActionsProvider { - + typealias AccessibilityModifierType = AccessibilityModifier.Type /// Type of modifier that will be used to apply strategies to view. Default it's internal implementation @@ -45,7 +45,6 @@ public extension AccessibilityItem { #endif } - var shouldOverrideStateTraits: Bool { false } func accessibilityStrategyConflictResolver(itemStrategy: AccessibilityStringStrategy, diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityModifier.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityModifier.swift index 741976731..4cf94f80d 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityModifier.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityModifier.swift @@ -18,9 +18,9 @@ public protocol AccessibilityModifier { /// Base modifier which handle only `AccessibilityItem` properties public enum BaseAccessibilityModifier: AccessibilityModifier { - static let stateTraits: UIAccessibilityTraits = [.selected, .notEnabled] + public static let stateTraits: UIAccessibilityTraits = [.selected, .notEnabled] - static public func modify(item: AccessibilityItem) { + public static func modify(item: AccessibilityItem) { guard !item.isAccessibilityIgnored else { return } @@ -45,7 +45,7 @@ public enum BaseAccessibilityModifier: AccessibilityModifier { item.accessibilityCustomActions = item.accessibilityActions() } - static public func modify(item: AccessibilityItem, generator: AccessibilityStrategyProvider) { + public static func modify(item: AccessibilityItem, generator: AccessibilityStrategyProvider) { guard !item.isAccessibilityIgnored || !generator.isAccessibilityIgnored else { return } @@ -85,7 +85,7 @@ public enum BaseAccessibilityModifier: AccessibilityModifier { /// Also adds trait `.staticText` for headers and footers to make them accessible by identifier public enum XCUITestsAccessibilityModifier: AccessibilityModifier { - static public func modify(item: AccessibilityItem) { + public static func modify(item: AccessibilityItem) { BaseAccessibilityModifier.modify(item: item) if item is UITableViewCell || item is UICollectionViewCell { item.accessibilityTraits.insert(.button) @@ -94,7 +94,7 @@ public enum XCUITestsAccessibilityModifier: AccessibilityModifier { } } - static public func modify(item: AccessibilityItem, generator: AccessibilityStrategyProvider) { + public static func modify(item: AccessibilityItem, generator: AccessibilityStrategyProvider) { BaseAccessibilityModifier.modify(item: item, generator: generator) if item is UITableViewCell || item is UICollectionViewCell { item.accessibilityTraits.insert(.button) diff --git a/Source/Table/Events/TableEvent.swift b/Source/Table/Events/TableEvent.swift index 0bf0f8093..1c6292cab 100644 --- a/Source/Table/Events/TableEvent.swift +++ b/Source/Table/Events/TableEvent.swift @@ -28,6 +28,7 @@ public enum TableEvent { case move(from: IndexPath, to: IndexPath) // MARK: - Accessibility Events + case invalidatedCellAccessibility(IndexPath, UITableViewCell) case invalidatedHeaderAccessibility(Int, UIView) case invalidatedFooterAccessibility(Int, UIView) From e085b8ae566215245e88c55457602ee9adaa5649 Mon Sep 17 00:00:00 2001 From: Sergey Korshunov Date: Fri, 2 Jun 2023 16:00:14 +0300 Subject: [PATCH 049/126] fixed swiftlint warnings 2 --- Source/Collection/Events/CollectionEvent.swift | 2 +- .../Plugins/Generators/Accessibility/AccessibilityItem.swift | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Collection/Events/CollectionEvent.swift b/Source/Collection/Events/CollectionEvent.swift index 727d2453a..1d160201e 100644 --- a/Source/Collection/Events/CollectionEvent.swift +++ b/Source/Collection/Events/CollectionEvent.swift @@ -20,7 +20,7 @@ public enum CollectionEvent { case move(from: IndexPath, to: IndexPath) // MARK: - Accessibility Events - + case invalidatedCellAccessibility(IndexPath, UICollectionViewCell) case invalidatedHeaderAccessibility(Int, UICollectionReusableView) case invalidatedFooterAccessibility(Int, UICollectionReusableView) diff --git a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift index dbe8c8932..7df422116 100644 --- a/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift +++ b/Source/Protocols/Plugins/Generators/Accessibility/AccessibilityItem.swift @@ -52,4 +52,3 @@ public extension AccessibilityItem { return [generatorStrategy, itemStrategy].compactMap(\.value).joined(separator: " ") } } - From a140e29bd683efcd4594888522371fd33e6c523a Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Thu, 8 Jun 2023 12:15:50 +0400 Subject: [PATCH 050/126] enable a11y audit (red = failed on dynamic size check) --- .../ReactiveDataDisplayManagerExampleUITests.swift | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/ReactiveDataDisplayManagerExampleUITests.swift b/Example/ReactiveDataDisplayManagerExampleUITests/ReactiveDataDisplayManagerExampleUITests.swift index c08a92f6b..ce43d6ea4 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/ReactiveDataDisplayManagerExampleUITests.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/ReactiveDataDisplayManagerExampleUITests.swift @@ -9,19 +9,24 @@ import XCTest class ReactiveDataDisplayManagerExampleUITests: BaseUITestCase { + override func setUpWithError() throws { + try super.setUpWithError() + continueAfterFailure = true + } + func testCollectionScreen() throws { setTab("Collection") - assertAllScreensOpeningWithoutCrashes() + try assertAllScreensOpeningWithoutCrashes() } func testTableScreen() throws { setTab("Table") - assertAllScreensOpeningWithoutCrashes() + try assertAllScreensOpeningWithoutCrashes() } func testStackScreen() throws { setTab("Stack") - assertAllScreensOpeningWithoutCrashes() + try assertAllScreensOpeningWithoutCrashes() } } @@ -29,11 +34,12 @@ class ReactiveDataDisplayManagerExampleUITests: BaseUITestCase { private extension ReactiveDataDisplayManagerExampleUITests { - func assertAllScreensOpeningWithoutCrashes() { + func assertAllScreensOpeningWithoutCrashes() throws { let tablesQuery = app.tables for i in 0...tablesQuery.cells.count - 1 { print("===== cell number: \(i) =====") tablesQuery.cells.element(boundBy: i).tap() + try app.performAccessibilityAudit() app.navigationBars.firstMatch.buttons["Back"].tap() } } From 01b510e44e3ca8b280b65515bc0280fbc4ed3be6 Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Thu, 8 Jun 2023 14:59:18 +0400 Subject: [PATCH 051/126] add missed a11y label to resolve sufficientElementDescription issue --- .../DiffableCollectionViewController.swift | 1 + .../DifferenceCollectionViewController.swift | 5 +++++ .../DiffableTableViewController.swift | 1 + 3 files changed, 7 insertions(+) diff --git a/Example/ReactiveDataDisplayManager/Collection/DiffableCollectionViewController/DiffableCollectionViewController.swift b/Example/ReactiveDataDisplayManager/Collection/DiffableCollectionViewController/DiffableCollectionViewController.swift index 9f14fe391..f8c54786c 100644 --- a/Example/ReactiveDataDisplayManager/Collection/DiffableCollectionViewController/DiffableCollectionViewController.swift +++ b/Example/ReactiveDataDisplayManager/Collection/DiffableCollectionViewController/DiffableCollectionViewController.swift @@ -89,6 +89,7 @@ private extension DiffableCollectionViewController { func setupSearch() { let searchBar = UISearchBar() searchBar.delegate = self + searchBar.searchTextField.accessibilityLabel = "Search field" navigationItem.titleView = searchBar } diff --git a/Example/ReactiveDataDisplayManager/Collection/DifferenceCollectionViewController/DifferenceCollectionViewController.swift b/Example/ReactiveDataDisplayManager/Collection/DifferenceCollectionViewController/DifferenceCollectionViewController.swift index 1200cda61..9379a8480 100644 --- a/Example/ReactiveDataDisplayManager/Collection/DifferenceCollectionViewController/DifferenceCollectionViewController.swift +++ b/Example/ReactiveDataDisplayManager/Collection/DifferenceCollectionViewController/DifferenceCollectionViewController.swift @@ -81,6 +81,11 @@ private extension DifferenceCollectionViewController { func setupSearch() { let searchBar = UISearchBar() searchBar.delegate = self + if #available(iOS 13.0, *) { + searchBar.searchTextField.accessibilityLabel = "Search field" + } else { + // Fallback on earlier versions + } navigationItem.titleView = searchBar } diff --git a/Example/ReactiveDataDisplayManager/Table/DiffableTableViewController/DiffableTableViewController.swift b/Example/ReactiveDataDisplayManager/Table/DiffableTableViewController/DiffableTableViewController.swift index 96a68ee1c..657f81e9b 100644 --- a/Example/ReactiveDataDisplayManager/Table/DiffableTableViewController/DiffableTableViewController.swift +++ b/Example/ReactiveDataDisplayManager/Table/DiffableTableViewController/DiffableTableViewController.swift @@ -91,6 +91,7 @@ private extension DiffableTableViewController { func setupSearch() { let searchBar = UISearchBar() searchBar.delegate = self + searchBar.searchTextField.accessibilityLabel = "Search field" navigationItem.titleView = searchBar } From 5b52a375f28feb48dbef64549f78f11c2f785d55 Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Thu, 8 Jun 2023 15:25:08 +0400 Subject: [PATCH 052/126] change screen titles to resolve textClipped issue --- ...AndDroppableCollectionViewController.swift | 2 +- .../ItemTitleCollectionViewController.swift | 2 +- .../MainCollectionViewController.swift | 16 +++++------ .../MovableCollectionViewController.swift | 2 +- .../PaginatableCollectionViewController.swift | 2 +- ...wipeableCollectionListViewController.swift | 2 +- .../AlphabeticalTableViewController.swift | 2 +- .../DiffableTableViewController.swift | 2 +- .../DifferenceTableViewController.swift | 2 +- .../DragAndDroppableTableViewController.swift | 2 +- .../GravityTableViewController.swift | 2 +- .../MainTableViewController.swift | 28 +++++++++---------- .../MovableTableViewController.swift | 2 +- .../PaginatableTableViewController.swift | 2 +- .../RefreshableTableViewController.swift | 2 +- .../SectionTitleTableViewController.swift | 2 +- .../SelectableTableViewController.swift | 2 +- .../SwipeableTableViewController.swift | 2 +- .../DragAndDroppablePluginExampleUITest.swift | 4 +-- .../Plugins/FoldablePluginExampleUITest.swift | 2 +- .../HighlightablePluginExampleUITest.swift | 4 +-- .../Plugins/MovablePluginExampleUITest.swift | 4 +-- .../PaginatablePluginExampleUITest.swift | 8 +++--- .../RefreshablePluginExampleUITest.swift | 2 +- .../SelectablePluginExampleUITest.swift | 4 +-- .../SwipeActionsPluginExampleUITest.swift | 8 +++--- .../Stress/AllPluginsStressTest.swift | 2 +- 27 files changed, 57 insertions(+), 57 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Collection/DragAndDroppableCollectionViewController/DragAndDroppableCollectionViewController.swift b/Example/ReactiveDataDisplayManager/Collection/DragAndDroppableCollectionViewController/DragAndDroppableCollectionViewController.swift index 6d54cdc6a..88e041e11 100644 --- a/Example/ReactiveDataDisplayManager/Collection/DragAndDroppableCollectionViewController/DragAndDroppableCollectionViewController.swift +++ b/Example/ReactiveDataDisplayManager/Collection/DragAndDroppableCollectionViewController/DragAndDroppableCollectionViewController.swift @@ -43,7 +43,7 @@ final class DragAndDroppableCollectionViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = "Collection with drag'n'drop items" + title = "drag'n'drop items" configureLayoutFlow() collectionView.accessibilityIdentifier = "Collection_with_drag_n_drop_items" diff --git a/Example/ReactiveDataDisplayManager/Collection/ItemTitleCollectionViewController/ItemTitleCollectionViewController.swift b/Example/ReactiveDataDisplayManager/Collection/ItemTitleCollectionViewController/ItemTitleCollectionViewController.swift index 2dc2c7d8b..9e9966c02 100644 --- a/Example/ReactiveDataDisplayManager/Collection/ItemTitleCollectionViewController/ItemTitleCollectionViewController.swift +++ b/Example/ReactiveDataDisplayManager/Collection/ItemTitleCollectionViewController/ItemTitleCollectionViewController.swift @@ -33,7 +33,7 @@ class ItemTitleCollectionViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = "Collection with item index titles" + title = "item index titles" configureLayoutFlow(with: appearance) updateBarButtonItem(with: appearance.title) diff --git a/Example/ReactiveDataDisplayManager/Collection/MainCollectionViewController/MainCollectionViewController.swift b/Example/ReactiveDataDisplayManager/Collection/MainCollectionViewController/MainCollectionViewController.swift index e54183dfd..3929bec8e 100644 --- a/Example/ReactiveDataDisplayManager/Collection/MainCollectionViewController/MainCollectionViewController.swift +++ b/Example/ReactiveDataDisplayManager/Collection/MainCollectionViewController/MainCollectionViewController.swift @@ -47,14 +47,14 @@ final class MainCollectionViewController: UIViewController { ("Horizontal image collection", .imageHorizontalCollection), ("Sizable collection", .sizableCollection), ("Foldable collection", .foldableCollection), - ("Collection with item index titles", .itemTitleCollection), - ("Collection with diffableDataSource", .diffableCollection), - ("Collection with pagination", .paginatableCollection), - ("Collection with compositional layout", .compositionalCollection), - ("Collection with DifferenceKit", .differenceCollection), - ("List Appearances with swipeable items", .swipeableListAppearances), - ("Collection with movable items", .movableCollection), - ("Collection with drag and drop item", .dragAndDroppableCollection), + ("item index titles", .itemTitleCollection), + ("diffableDataSource", .diffableCollection), + ("pagination", .paginatableCollection), + ("compositional layout", .compositionalCollection), + ("DifferenceKit", .differenceCollection), + ("list with swipes", .swipeableListAppearances), + ("movable items", .movableCollection), + ("drag and drop item", .dragAndDroppableCollection), ("Carousel collection view layout", .carouselCollection), ("Aligned collection layout", .alignedCollection), ("Dynamic height ViewController", .dynamicHeightViewController) diff --git a/Example/ReactiveDataDisplayManager/Collection/MovableCollectionViewController/MovableCollectionViewController.swift b/Example/ReactiveDataDisplayManager/Collection/MovableCollectionViewController/MovableCollectionViewController.swift index 9a639996d..221d4af83 100644 --- a/Example/ReactiveDataDisplayManager/Collection/MovableCollectionViewController/MovableCollectionViewController.swift +++ b/Example/ReactiveDataDisplayManager/Collection/MovableCollectionViewController/MovableCollectionViewController.swift @@ -47,7 +47,7 @@ final class MovableCollectionViewController: UIViewController { private extension MovableCollectionViewController { func setupInitialState() { - title = "Collection with movable cell" + title = "movable cell" configureCollectionView() fillAdapter() diff --git a/Example/ReactiveDataDisplayManager/Collection/PaginatableCollectionViewController/PaginatableCollectionViewController.swift b/Example/ReactiveDataDisplayManager/Collection/PaginatableCollectionViewController/PaginatableCollectionViewController.swift index dc94b3625..ec40bd126 100644 --- a/Example/ReactiveDataDisplayManager/Collection/PaginatableCollectionViewController/PaginatableCollectionViewController.swift +++ b/Example/ReactiveDataDisplayManager/Collection/PaginatableCollectionViewController/PaginatableCollectionViewController.swift @@ -40,7 +40,7 @@ final class PaginatableCollectionViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = "Collection with pagination" + title = "pagination" configureActivityIndicatorIfNeeded() loadFirstPage() diff --git a/Example/ReactiveDataDisplayManager/Collection/SwipeableCollectionListViewController/SwipeableCollectionListViewController.swift b/Example/ReactiveDataDisplayManager/Collection/SwipeableCollectionListViewController/SwipeableCollectionListViewController.swift index 9ff28c8f8..7d508e47d 100644 --- a/Example/ReactiveDataDisplayManager/Collection/SwipeableCollectionListViewController/SwipeableCollectionListViewController.swift +++ b/Example/ReactiveDataDisplayManager/Collection/SwipeableCollectionListViewController/SwipeableCollectionListViewController.swift @@ -36,7 +36,7 @@ final class SwipeableCollectionListViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = "List Appearances with swipeable items" + title = "list with swipes" fillAdapter() configureLayoutFlow(with: appearance) diff --git a/Example/ReactiveDataDisplayManager/Table/AlphabeticalTableViewController/AlphabeticalTableViewController.swift b/Example/ReactiveDataDisplayManager/Table/AlphabeticalTableViewController/AlphabeticalTableViewController.swift index d34866f3c..122a0b92b 100644 --- a/Example/ReactiveDataDisplayManager/Table/AlphabeticalTableViewController/AlphabeticalTableViewController.swift +++ b/Example/ReactiveDataDisplayManager/Table/AlphabeticalTableViewController/AlphabeticalTableViewController.swift @@ -33,7 +33,7 @@ final class AlphabeticalTableViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = "Table with alphabetize sections" + title = "alphabetize sections" fillAdapter() } diff --git a/Example/ReactiveDataDisplayManager/Table/DiffableTableViewController/DiffableTableViewController.swift b/Example/ReactiveDataDisplayManager/Table/DiffableTableViewController/DiffableTableViewController.swift index 657f81e9b..4818608b3 100644 --- a/Example/ReactiveDataDisplayManager/Table/DiffableTableViewController/DiffableTableViewController.swift +++ b/Example/ReactiveDataDisplayManager/Table/DiffableTableViewController/DiffableTableViewController.swift @@ -53,7 +53,7 @@ final class DiffableTableViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = "Table with diffableDataSource" + title = "diffableDataSource" setupSearch() setupBarButtonItem() diff --git a/Example/ReactiveDataDisplayManager/Table/DifferenceTableViewController/DifferenceTableViewController.swift b/Example/ReactiveDataDisplayManager/Table/DifferenceTableViewController/DifferenceTableViewController.swift index 010dfd145..71f7ca3e8 100644 --- a/Example/ReactiveDataDisplayManager/Table/DifferenceTableViewController/DifferenceTableViewController.swift +++ b/Example/ReactiveDataDisplayManager/Table/DifferenceTableViewController/DifferenceTableViewController.swift @@ -30,7 +30,7 @@ final class DifferenceTableViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = "table with DifferenceKit" + title = "DifferenceKit" fillAdapter() } diff --git a/Example/ReactiveDataDisplayManager/Table/DragAndDroppableTableViewController/DragAndDroppableTableViewController.swift b/Example/ReactiveDataDisplayManager/Table/DragAndDroppableTableViewController/DragAndDroppableTableViewController.swift index f5ab46eec..eefb80ec6 100644 --- a/Example/ReactiveDataDisplayManager/Table/DragAndDroppableTableViewController/DragAndDroppableTableViewController.swift +++ b/Example/ReactiveDataDisplayManager/Table/DragAndDroppableTableViewController/DragAndDroppableTableViewController.swift @@ -38,7 +38,7 @@ final class DragAndDroppableTableViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = "table with drag'n'drop cell" + title = "drag'n'drop cell" tableView.accessibilityIdentifier = "Table_with_drag_n_drop_cell" tableView.dragInteractionEnabled = true diff --git a/Example/ReactiveDataDisplayManager/Table/GravityTableViewController/GravityTableViewController.swift b/Example/ReactiveDataDisplayManager/Table/GravityTableViewController/GravityTableViewController.swift index f84d874b7..7cb7e72ac 100644 --- a/Example/ReactiveDataDisplayManager/Table/GravityTableViewController/GravityTableViewController.swift +++ b/Example/ReactiveDataDisplayManager/Table/GravityTableViewController/GravityTableViewController.swift @@ -25,7 +25,7 @@ final class GravityTableViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = "Gravity table with foldable cell" + title = "Gravity foldable cell" fillAdapter() } diff --git a/Example/ReactiveDataDisplayManager/Table/MainTableViewController/MainTableViewController.swift b/Example/ReactiveDataDisplayManager/Table/MainTableViewController/MainTableViewController.swift index c333aadf4..3cfb2a886 100644 --- a/Example/ReactiveDataDisplayManager/Table/MainTableViewController/MainTableViewController.swift +++ b/Example/ReactiveDataDisplayManager/Table/MainTableViewController/MainTableViewController.swift @@ -38,20 +38,20 @@ final class MainTableViewController: UIViewController { static let models: [(title: String, segueId: SegueIdentifier)] = [ ("Gallery without prefetching", .imageTable), ("Gallery with prefetching", .prefetchingTable), - ("Table with foldable cell", .foldableCellTable), - ("Gravity table with foldable cell", .gravityTable), - ("Table with movable cell", .movableTable), - ("Table with alphabetize sections", .alphabetizeSectionsTable), - ("Table with sections titles", .sectionTitlesTable), - ("Table with diffableDataSource", .diffableTable), - ("Table with swipeable cells", .swipeableTable), - ("Table with refresh control", .refreshableTable), - ("Table with pagination", .paginatableTable), - ("Table with all plugins", .allPluginsTable), - ("Table with DifferenceKit", .differenceTable), - ("Table with drag and drop cells", .dragAndDroppableTable), - ("Table with selectable cells", .selectableTable), - ("Table with highlightable cells", .highlightableTable) + ("foldable cell", .foldableCellTable), + ("Gravity foldable cell", .gravityTable), + ("movable cell", .movableTable), + ("alphabetize sections", .alphabetizeSectionsTable), + ("sections titles", .sectionTitlesTable), + ("diffableDataSource", .diffableTable), + ("swipeable cells", .swipeableTable), + ("refresh control", .refreshableTable), + ("pagination", .paginatableTable), + ("all plugins", .allPluginsTable), + ("DifferenceKit", .differenceTable), + ("drag and drop cells", .dragAndDroppableTable), + ("selectable cells", .selectableTable), + ("highlightable cells", .highlightableTable) ] } diff --git a/Example/ReactiveDataDisplayManager/Table/MovableTableViewController/MovableTableViewController.swift b/Example/ReactiveDataDisplayManager/Table/MovableTableViewController/MovableTableViewController.swift index ec2ec2c8f..3dc49b1ad 100644 --- a/Example/ReactiveDataDisplayManager/Table/MovableTableViewController/MovableTableViewController.swift +++ b/Example/ReactiveDataDisplayManager/Table/MovableTableViewController/MovableTableViewController.swift @@ -32,7 +32,7 @@ final class MovableTableViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = "table with movable cell" + title = "movable cell" tableView.accessibilityIdentifier = "Table_with_movable_cell" tableView.dragInteractionEnabled = false fillAdapter() diff --git a/Example/ReactiveDataDisplayManager/Table/PaginatableTableViewController/PaginatableTableViewController.swift b/Example/ReactiveDataDisplayManager/Table/PaginatableTableViewController/PaginatableTableViewController.swift index 4c9d72357..f1e4723bd 100644 --- a/Example/ReactiveDataDisplayManager/Table/PaginatableTableViewController/PaginatableTableViewController.swift +++ b/Example/ReactiveDataDisplayManager/Table/PaginatableTableViewController/PaginatableTableViewController.swift @@ -41,7 +41,7 @@ final class PaginatableTableViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = "Table with pagination" + title = "pagination" configureActivityIndicatorIfNeeded() loadFirstPage() diff --git a/Example/ReactiveDataDisplayManager/Table/RefreshableTableViewController/RefreshableTableViewController.swift b/Example/ReactiveDataDisplayManager/Table/RefreshableTableViewController/RefreshableTableViewController.swift index 1c63e05df..92d21cf3e 100644 --- a/Example/ReactiveDataDisplayManager/Table/RefreshableTableViewController/RefreshableTableViewController.swift +++ b/Example/ReactiveDataDisplayManager/Table/RefreshableTableViewController/RefreshableTableViewController.swift @@ -32,7 +32,7 @@ final class RefreshableTableViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = "Table with refresh control" + title = "refresh control" tableView.accessibilityIdentifier = "Table_with_refresh_control" fillAdapter() } diff --git a/Example/ReactiveDataDisplayManager/Table/SectionTitleTableViewController/SectionTitleTableViewController.swift b/Example/ReactiveDataDisplayManager/Table/SectionTitleTableViewController/SectionTitleTableViewController.swift index 8698b7567..b0af792a0 100644 --- a/Example/ReactiveDataDisplayManager/Table/SectionTitleTableViewController/SectionTitleTableViewController.swift +++ b/Example/ReactiveDataDisplayManager/Table/SectionTitleTableViewController/SectionTitleTableViewController.swift @@ -45,7 +45,7 @@ final class SectionTitleTableViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = "Table with section title" + title = "section title" fillAdapter() } diff --git a/Example/ReactiveDataDisplayManager/Table/SelectableTableViewController/SelectableTableViewController.swift b/Example/ReactiveDataDisplayManager/Table/SelectableTableViewController/SelectableTableViewController.swift index 11cc8ed6d..f1ccd4c57 100644 --- a/Example/ReactiveDataDisplayManager/Table/SelectableTableViewController/SelectableTableViewController.swift +++ b/Example/ReactiveDataDisplayManager/Table/SelectableTableViewController/SelectableTableViewController.swift @@ -32,7 +32,7 @@ final class SelectableTableViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = "Table with selectable cells" + title = "selectable cells" tableView.accessibilityIdentifier = "Table_with_selectable_cells" fillAdapter() updateBarButtonItem(with: Constants.standart) diff --git a/Example/ReactiveDataDisplayManager/Table/SwipeableTableViewController/SwipeableTableViewController.swift b/Example/ReactiveDataDisplayManager/Table/SwipeableTableViewController/SwipeableTableViewController.swift index 31333591d..0a26af669 100644 --- a/Example/ReactiveDataDisplayManager/Table/SwipeableTableViewController/SwipeableTableViewController.swift +++ b/Example/ReactiveDataDisplayManager/Table/SwipeableTableViewController/SwipeableTableViewController.swift @@ -32,7 +32,7 @@ final class SwipeableTableViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = "Table with swipeable cells" + title = "swipeable cells" fillAdapter() } diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/DragAndDroppablePluginExampleUITest.swift b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/DragAndDroppablePluginExampleUITest.swift index 68e5f72a9..d8d7008a1 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/DragAndDroppablePluginExampleUITest.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/DragAndDroppablePluginExampleUITest.swift @@ -21,7 +21,7 @@ final class DragAndDroppablePluginExampleUITest: BaseUITestCase { let duration = Constants.dragDuration setTab("Collection") - tapTableElement("Collection with drag and drop item") + tapTableElement("drag and drop item") let sourceCell = getCell(for: .collection, collectionId: collectionId, cellId: sourceDraggable) let destinationCell = getCell(for: .collection, collectionId: collectionId, cellId: destinationDraggable) @@ -40,7 +40,7 @@ final class DragAndDroppablePluginExampleUITest: BaseUITestCase { let duration = Constants.dragDuration setTab("Table") - tapTableElement("Table with drag and drop cells") + tapTableElement("drag and drop cells") let sourceCell = getCell(for: .table, collectionId: tableId, cellId: sourceDraggable) let destinationCell = getCell(for: .table, collectionId: tableId, cellId: destinationDraggable) diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/FoldablePluginExampleUITest.swift b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/FoldablePluginExampleUITest.swift index b78069a3c..6d5554a97 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/FoldablePluginExampleUITest.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/FoldablePluginExampleUITest.swift @@ -13,7 +13,7 @@ final class FoldablePluginExampleUITest: BaseUITestCase { let tableId = "FoldableTableViewController" setTab("Table") - tapTableElement("Table with foldable cell") + tapTableElement("foldable cell") let foldable = getCell(for: .table, collectionId: tableId, cellId: "Foldable cell 1") foldable.tap() diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/HighlightablePluginExampleUITest.swift b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/HighlightablePluginExampleUITest.swift index a5527d475..0de7ac8c8 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/HighlightablePluginExampleUITest.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/HighlightablePluginExampleUITest.swift @@ -17,7 +17,7 @@ final class HighlightablePluginExampleUITest: BaseUITestCase { var wasPresed = false setTab("Table") - tapTableElement("Table with highlightable cells") + tapTableElement("highlightable cells") let cell = getFirstCell(for: .table, id: "Higlighted_cells") @@ -37,7 +37,7 @@ final class HighlightablePluginExampleUITest: BaseUITestCase { let normalStyle = "Normal" setTab("Table") - tapTableElement("Table with highlightable cells") + tapTableElement("highlightable cells") tapButton("Single mode") let cell = getFirstCell(for: .table, id: "Higlighted_cells") diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/MovablePluginExampleUITest.swift b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/MovablePluginExampleUITest.swift index e2c7277b9..fbc0b9c0d 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/MovablePluginExampleUITest.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/MovablePluginExampleUITest.swift @@ -21,7 +21,7 @@ final class MovablePluginExampleUITest: BaseUITestCase { let duration = Constants.dragDuration setTab("Collection") - tapTableElement("Collection with movable items") + tapTableElement("movable items") let sourceCell = getCell(for: .collection, collectionId: collectionId, cellId: sourceDraggable) let destinationCell = getCell(for: .collection, collectionId: collectionId, cellId: destinationDraggable) @@ -39,7 +39,7 @@ final class MovablePluginExampleUITest: BaseUITestCase { let duration = Constants.dragDuration setTab("Table") - tapTableElement("Table with movable cell") + tapTableElement("movable cell") let sourceCell = getCell(for: .table, collectionId: tableId, cellId: sourceDraggable) let destinationSection = app.tables[tableId].otherElements["Section 2"].firstMatch diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/PaginatablePluginExampleUITest.swift b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/PaginatablePluginExampleUITest.swift index 0429ee532..7b58c8614 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/PaginatablePluginExampleUITest.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/PaginatablePluginExampleUITest.swift @@ -18,7 +18,7 @@ final class PaginatablePluginExampleUITest: BaseUITestCase { let pageSize = 16 setTab("Table") - tapTableElement("Table with pagination") + tapTableElement("pagination") let table = app.tables.firstMatch let retryButton = app.buttons["Retry"] @@ -42,7 +42,7 @@ final class PaginatablePluginExampleUITest: BaseUITestCase { // Description: In a collection, the first cell is the first visible cell func testCollection_whenSwipeUp_thenFirstVisibleCellChanged() throws { setTab("Collection") - tapTableElement("Collection with pagination") + tapTableElement("pagination") let collection = app.collectionViews.firstMatch let firstCell = app.collectionViews.cells.firstMatch @@ -67,7 +67,7 @@ final class PaginatablePluginExampleUITest: BaseUITestCase { let retryButton = app.buttons["Retry"] setTab("Table") - tapTableElement("Table with pagination") + tapTableElement("pagination") let table = app.tables.firstMatch XCTAssertTrue(table.waitForExistence(timeout: Constants.timeout)) @@ -90,7 +90,7 @@ final class PaginatablePluginExampleUITest: BaseUITestCase { let retryButton = app.buttons["Retry"] setTab("Collection") - tapTableElement("Collection with pagination") + tapTableElement("pagination") let collection = app.collectionViews.firstMatch XCTAssertTrue(collection.waitForExistence(timeout: Constants.timeout)) diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/RefreshablePluginExampleUITest.swift b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/RefreshablePluginExampleUITest.swift index a93d72301..303e35920 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/RefreshablePluginExampleUITest.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/RefreshablePluginExampleUITest.swift @@ -17,7 +17,7 @@ final class RefreshablePluginExampleUITest: BaseUITestCase { func testTable_whenScrollDown_thenShowRefreshControl() throws { setTab("Table") - tapTableElement("Table with refresh control") + tapTableElement("refresh control") let cell = getFirstCell(for: .table, id: "Table_with_refresh_control") let refreshControl = app.otherElements["RefreshableTableViewController_RefreshControl"] diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/SelectablePluginExampleUITest.swift b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/SelectablePluginExampleUITest.swift index b9e267f43..9115e1626 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/SelectablePluginExampleUITest.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/SelectablePluginExampleUITest.swift @@ -11,7 +11,7 @@ final class SelectablePluginExampleUITest: BaseUITestCase { func testTableMultipleTap_whenCellTaped_thenCellSelected_thenCellDeselected() throws { setTab("Table") - tapTableElement("Table with selectable cells") + tapTableElement("selectable cells") tapButton("Single mode") let cell = getFirstCell(for: .table, id: "Table_with_selectable_cells") @@ -25,7 +25,7 @@ final class SelectablePluginExampleUITest: BaseUITestCase { func testTableSingleTap_whenCellTaped_thenCellNotSelected() throws { setTab("Table") - tapTableElement("Table with selectable cells") + tapTableElement("selectable cells") let cell = getFirstCell(for: .table, id: "Table_with_selectable_cells") diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/SwipeActionsPluginExampleUITest.swift b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/SwipeActionsPluginExampleUITest.swift index c906f6ac1..3ed179ea8 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/SwipeActionsPluginExampleUITest.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/Plugins/SwipeActionsPluginExampleUITest.swift @@ -11,7 +11,7 @@ final class SwipeActionsPluginExampleUITest: BaseUITestCase { func testCollection_whenSwipeLeftFirstCell_thenCellHaveThreeButtons() throws { setTab("Collection") - tapTableElement("List Appearances with swipeable items") + tapTableElement("list with swipes") let collectionView = app.collectionViews.firstMatch let cell = collectionView.firstMatch.cells.firstMatch @@ -29,7 +29,7 @@ final class SwipeActionsPluginExampleUITest: BaseUITestCase { func testCollection_whenSwipeRightFirstCell_thenCellHaveThreeButtons() throws { setTab("Collection") - tapTableElement("List Appearances with swipeable items") + tapTableElement("list with swipes") let collectionView = app.collectionViews.firstMatch let cell = collectionView.firstMatch.cells.firstMatch @@ -49,7 +49,7 @@ final class SwipeActionsPluginExampleUITest: BaseUITestCase { func testTable_whenSwipeLeftFirstCell_thenCellHaveThreeButtons() throws { setTab("Table") - tapTableElement("Table with swipeable cells") + tapTableElement("swipeable cells") let cell = app.tables.firstMatch.cells.firstMatch @@ -66,7 +66,7 @@ final class SwipeActionsPluginExampleUITest: BaseUITestCase { func testTable_whenSwipeRightFirstCell_thenCellHaveThreeButtons() throws { setTab("Table") - tapTableElement("Table with swipeable cells") + tapTableElement("swipeable cells") let cell = app.tables.firstMatch.cells.firstMatch diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/Stress/AllPluginsStressTest.swift b/Example/ReactiveDataDisplayManagerExampleUITests/Stress/AllPluginsStressTest.swift index 4a70bf6f0..c4038dcd6 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/Stress/AllPluginsStressTest.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/Stress/AllPluginsStressTest.swift @@ -18,7 +18,7 @@ final class AllPluginsStressTest: BaseUITestCase { func testTable_whenAllPluginsScreenIsConstantlyUpdating_thenExpandableCellDidntCrashing() { setTab("Table") - tapTableElement("Table with all plugins") + tapTableElement("all plugins") let cell = getCell(for: .table, collectionId: "main_table", cellId: "expandable_cell") From e82081f728e8a20e6e411bd59f8e4c5390df2ea7 Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Thu, 8 Jun 2023 15:31:08 +0400 Subject: [PATCH 053/126] extend size of title collection header generator --- .../Generators/TitleCollectionHeaderGenerator.swift | 2 +- .../TitleCollectionReusableView.xib | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/Generators/TitleCollectionHeaderGenerator.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/Generators/TitleCollectionHeaderGenerator.swift index a652f2291..e5eeda9a2 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/Generators/TitleCollectionHeaderGenerator.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/Generators/TitleCollectionHeaderGenerator.swift @@ -31,7 +31,7 @@ extension TitleCollectionHeaderGenerator: CollectionHeaderGenerator { } func size(_ collectionView: UICollectionView, forSection section: Int) -> CGSize { - return CGSize(width: collectionView.bounds.width, height: 44.0) + return CGSize(width: collectionView.bounds.width, height: 56.0) } } diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/TitleCollectionReusableView/TitleCollectionReusableView.xib b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/TitleCollectionReusableView/TitleCollectionReusableView.xib index ba095fb59..026586b52 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/TitleCollectionReusableView/TitleCollectionReusableView.xib +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/TitleCollectionReusableView/TitleCollectionReusableView.xib @@ -1,9 +1,9 @@ - + - + @@ -15,7 +15,7 @@ - + - + From e951741236dad421c272a987726246db334efa8c Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Thu, 8 Jun 2023 15:49:04 +0400 Subject: [PATCH 054/126] enable fonts from system category --- .../SizableCollectionViewCell.swift | 2 +- .../Stack/Landing/LandingStackViewController.swift | 7 ++++--- .../Stack/Views/Generators/TitleStackCellGenerator.swift | 2 +- .../Cells/ImageTableViewCell/ImageTableViewCell.swift | 2 +- .../Table/Views/Headers/HeaderView/HeaderView.swift | 2 +- .../BaseStackDataDisplatManagerTests.swift | 2 +- .../Stack/Manager/BaseStackManagerTests.swift | 2 +- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.swift index 754a23027..579d47cd1 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.swift @@ -14,7 +14,7 @@ final class SizableCollectionViewCell: UICollectionViewCell { // MARK: - Constants private enum Constants { - static let titleFont = UIFont.systemFont(ofSize: 15.0) + static let titleFont = UIFont.preferredFont(forTextStyle: .subheadline) } // MARK: - IBOutlet diff --git a/Example/ReactiveDataDisplayManager/Stack/Landing/LandingStackViewController.swift b/Example/ReactiveDataDisplayManager/Stack/Landing/LandingStackViewController.swift index 82bb3e7ba..312c115e2 100644 --- a/Example/ReactiveDataDisplayManager/Stack/Landing/LandingStackViewController.swift +++ b/Example/ReactiveDataDisplayManager/Stack/Landing/LandingStackViewController.swift @@ -54,12 +54,13 @@ private extension LandingStackViewController { let sampleText = "LongText".localized // Create generators + let title = TextStackCellGenerator(model: .init(title: "Title text", - alignment: .left, - font: .systemFont(ofSize: 43, weight: .bold))) + alignment: .left, + font: .preferredFont(forTextStyle: .largeTitle))) let description = TextStackCellGenerator(model: .init(title: sampleText, alignment: .left, - font: .systemFont(ofSize: 30))) + font: .preferredFont(forTextStyle: .title1))) let mainButton = ButtonStackCellGenerator(model: .init(title: "Main", titleColor: .black, diff --git a/Example/ReactiveDataDisplayManager/Stack/Views/Generators/TitleStackCellGenerator.swift b/Example/ReactiveDataDisplayManager/Stack/Views/Generators/TitleStackCellGenerator.swift index a55309bf5..c93ab1c58 100644 --- a/Example/ReactiveDataDisplayManager/Stack/Views/Generators/TitleStackCellGenerator.swift +++ b/Example/ReactiveDataDisplayManager/Stack/Views/Generators/TitleStackCellGenerator.swift @@ -29,7 +29,7 @@ extension TitleStackCellGenerator: ViewBuilder { func build(view: UILabel) { view.text = title - view.font = UIFont.systemFont(ofSize: 34.0, weight: .bold) + view.font = UIFont.preferredFont(forTextStyle: .largeTitle) } } diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.swift index 5e5984d2c..771e57a3f 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.swift @@ -29,7 +29,7 @@ final class ImageTableViewCell: UITableViewCell { // MARK: - Constants private enum Constants { - static let titleFont: UIFont = .systemFont(ofSize: 15, weight: .semibold) + static let titleFont: UIFont = .preferredFont(forTextStyle: .subheadline) static let cornerRadius: CGFloat = 10 } diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Headers/HeaderView/HeaderView.swift b/Example/ReactiveDataDisplayManager/Table/Views/Headers/HeaderView/HeaderView.swift index 9aca8bcef..4f0b1f957 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Headers/HeaderView/HeaderView.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Headers/HeaderView/HeaderView.swift @@ -35,7 +35,7 @@ final class HeaderView: UIView, AccessibilityItem { func configure(with title: String) { titleLabel.text = title - titleLabel.font = .systemFont(ofSize: 18, weight: .bold) + titleLabel.font = .preferredFont(forTextStyle: .headline) } } diff --git a/ReactiveDataDisplayManagerTests/BaseStackDataDisplatManagerTests.swift b/ReactiveDataDisplayManagerTests/BaseStackDataDisplatManagerTests.swift index 86238d4d4..1d7a888ea 100644 --- a/ReactiveDataDisplayManagerTests/BaseStackDataDisplatManagerTests.swift +++ b/ReactiveDataDisplayManagerTests/BaseStackDataDisplatManagerTests.swift @@ -27,7 +27,7 @@ extension TitleStackCellGenerator: ViewBuilder { func build(view: UILabel) { self.view = view view.text = title - view.font = UIFont.systemFont(ofSize: 34.0, weight: .bold) + view.font = UIFont.preferredFont(forTextStyle: .largeTitle) } } diff --git a/ReactiveDataDisplayManagerTests/Stack/Manager/BaseStackManagerTests.swift b/ReactiveDataDisplayManagerTests/Stack/Manager/BaseStackManagerTests.swift index f5ed4c338..e20843926 100644 --- a/ReactiveDataDisplayManagerTests/Stack/Manager/BaseStackManagerTests.swift +++ b/ReactiveDataDisplayManagerTests/Stack/Manager/BaseStackManagerTests.swift @@ -182,7 +182,7 @@ extension MockStackCellGenerator: ViewBuilder { func build(view: UILabel) { self.view = view view.text = title - view.font = UIFont.systemFont(ofSize: 34.0, weight: .bold) + view.font = UIFont.preferredFont(forTextStyle: .largeTitle) } } From c3c9b311d341e0f260aa57ae337fa97eea36b6df Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Thu, 8 Jun 2023 17:14:07 +0400 Subject: [PATCH 055/126] resolve dynamic size issues for main screen --- .../MainTableViewController/MainTableViewController.swift | 2 +- .../TitleWithIconTableViewCell.swift | 3 ++- .../TitleWithIconTableViewCell.xib | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Table/MainTableViewController/MainTableViewController.swift b/Example/ReactiveDataDisplayManager/Table/MainTableViewController/MainTableViewController.swift index 3cfb2a886..ef75ded22 100644 --- a/Example/ReactiveDataDisplayManager/Table/MainTableViewController/MainTableViewController.swift +++ b/Example/ReactiveDataDisplayManager/Table/MainTableViewController/MainTableViewController.swift @@ -83,7 +83,7 @@ private extension MainTableViewController { for model in Constants.models { // Create generator - let generator = TitleWithIconTableViewCell.rddm.calculatableHeightGenerator(with: model.title) + let generator = TitleWithIconTableViewCell.rddm.baseGenerator(with: model.title) generator.didSelectEvent += { [weak self] in self?.openScreen(by: model.segueId) diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleWithIconTableViewCell/TitleWithIconTableViewCell.swift b/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleWithIconTableViewCell/TitleWithIconTableViewCell.swift index 8757f1f51..9fd9d738a 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleWithIconTableViewCell/TitleWithIconTableViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleWithIconTableViewCell/TitleWithIconTableViewCell.swift @@ -18,7 +18,8 @@ class TitleWithIconTableViewCell: UITableViewCell, CalculatableHeightItem { // MARK: - CalculatableHeightItem static func getHeight(forWidth width: CGFloat, with model: String) -> CGFloat { - return 44 + return model.getHeight(withConstrainedWidth: UIScreen.main.bounds.width, + font: .preferredFont(forTextStyle: .body)) } } diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleWithIconTableViewCell/TitleWithIconTableViewCell.xib b/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleWithIconTableViewCell/TitleWithIconTableViewCell.xib index 27a825b48..420aa483c 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleWithIconTableViewCell/TitleWithIconTableViewCell.xib +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleWithIconTableViewCell/TitleWithIconTableViewCell.xib @@ -1,16 +1,16 @@ - + - + - + @@ -19,7 +19,7 @@ From 8b82b178d0ba90ea9054c1f6b52b32d1faeeb02c Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Fri, 9 Jun 2023 16:20:47 +0400 Subject: [PATCH 056/126] set textstyle for labels and buttons in xib (issues decreased from 229 to 57) --- .../Application/AppDelegate.swift | 2 ++ .../DynamicHeightTableViewCell.xib | 8 ++++---- ...FittingCompressedSizeCollectionViewCell.xib | 6 +++--- .../SizableCollectionViewCell.xib | 8 ++++---- .../TitleCollectionListCell.xib | 8 ++++---- .../TitleCollectionViewCell.xib | 18 +++++++++--------- .../Footer/TitleIconCollectionFooterView.xib | 12 ++++++------ .../HeaderCollectionListView.xib | 8 ++++---- .../TitleCollectionReusableView.xib | 2 +- .../ExpandableTableCell.xib | 7 ++++--- .../FoldableTableViewCell.xib | 8 ++++---- .../HighlightableTableCell.xib | 6 +++--- .../ImageTableViewCell/ImageTableViewCell.xib | 8 ++++---- .../TitleTableViewCell/TitleTableViewCell.xib | 6 +++--- 14 files changed, 55 insertions(+), 52 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Application/AppDelegate.swift b/Example/ReactiveDataDisplayManager/Application/AppDelegate.swift index eadca2071..66de21b82 100644 --- a/Example/ReactiveDataDisplayManager/Application/AppDelegate.swift +++ b/Example/ReactiveDataDisplayManager/Application/AppDelegate.swift @@ -19,6 +19,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { UITabBar.appearance().tintColor = .rddm UITableView.appearance().sectionIndexColor = .rddm UICollectionView.appearance().tintColor = .rddm + UILabel.appearance().adjustsFontForContentSizeCategory = true + UIButton.appearance().adjustsImageSizeForAccessibilityContentSizeCategory = true #if DEBUG if CommandLine.arguments.contains("-disableAnimations") { diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/DynamicHeightViewCell/DynamicHeightTableViewCell.xib b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/DynamicHeightViewCell/DynamicHeightTableViewCell.xib index 18eb627f8..d84bc3405 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/DynamicHeightViewCell/DynamicHeightTableViewCell.xib +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/DynamicHeightViewCell/DynamicHeightTableViewCell.xib @@ -1,9 +1,9 @@ - + - + @@ -30,13 +30,13 @@ diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FittingCompressedSizeCollectionViewCell/FittingCompressedSizeCollectionViewCell.xib b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FittingCompressedSizeCollectionViewCell/FittingCompressedSizeCollectionViewCell.xib index 63fc16654..5b000cb6c 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FittingCompressedSizeCollectionViewCell/FittingCompressedSizeCollectionViewCell.xib +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/FittingCompressedSizeCollectionViewCell/FittingCompressedSizeCollectionViewCell.xib @@ -1,9 +1,9 @@ - + - + @@ -19,7 +19,7 @@ diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.xib b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.xib index c4caf4425..5290ca9c5 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.xib +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.xib @@ -1,16 +1,16 @@ - + - + - + @@ -19,7 +19,7 @@ diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionListCell/TitleCollectionListCell.xib b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionListCell/TitleCollectionListCell.xib index 390e4b54b..caa632fff 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionListCell/TitleCollectionListCell.xib +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionListCell/TitleCollectionListCell.xib @@ -1,16 +1,16 @@ - + - + - + @@ -19,7 +19,7 @@ diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.xib b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.xib index 5c9d2c6c6..37269220a 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.xib +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/TitleCollectionViewCell/TitleCollectionViewCell.xib @@ -1,9 +1,9 @@ - + - + @@ -11,14 +11,14 @@ - + - + - + @@ -27,8 +27,8 @@ @@ -45,11 +45,11 @@ - + - + diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Footer/TitleIconCollectionFooterView.xib b/Example/ReactiveDataDisplayManager/Collection/Views/Footer/TitleIconCollectionFooterView.xib index 645b1ff7c..dac136a35 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Footer/TitleIconCollectionFooterView.xib +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Footer/TitleIconCollectionFooterView.xib @@ -1,27 +1,27 @@ - + - + - + - + diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/HeaderCollectionListView/HeaderCollectionListView.xib b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/HeaderCollectionListView/HeaderCollectionListView.xib index cdd0f0f24..7f3936dd3 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/HeaderCollectionListView/HeaderCollectionListView.xib +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/HeaderCollectionListView/HeaderCollectionListView.xib @@ -1,9 +1,9 @@ - + - + @@ -11,7 +11,7 @@ - + @@ -20,7 +20,7 @@ diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/TitleCollectionReusableView/TitleCollectionReusableView.xib b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/TitleCollectionReusableView/TitleCollectionReusableView.xib index 026586b52..25089c84b 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Headers/TitleCollectionReusableView/TitleCollectionReusableView.xib +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Headers/TitleCollectionReusableView/TitleCollectionReusableView.xib @@ -16,7 +16,7 @@ diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.xib b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.xib index 2c570dd6d..69d4b8717 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.xib +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ExpandableTableCell/ExpandableTableCell.xib @@ -1,9 +1,9 @@ - + - + @@ -26,6 +26,7 @@ + @@ -59,7 +60,7 @@ - + diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/FoldableTableViewCell/FoldableTableViewCell.xib b/Example/ReactiveDataDisplayManager/Table/Views/Cells/FoldableTableViewCell/FoldableTableViewCell.xib index c1e514525..ec7937adc 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/FoldableTableViewCell/FoldableTableViewCell.xib +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/FoldableTableViewCell/FoldableTableViewCell.xib @@ -1,16 +1,16 @@ - + - + - + @@ -19,7 +19,7 @@ diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.xib b/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.xib index 2556fe1b7..a05f847fe 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.xib +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/HighlightableTableCell/HighlightableTableCell.xib @@ -1,9 +1,9 @@ - + - + @@ -19,7 +19,7 @@ diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.xib b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.xib index aa5885a0b..a2bab5de3 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.xib +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.xib @@ -1,16 +1,16 @@ - + - + - + @@ -19,7 +19,7 @@ diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleTableViewCell/TitleTableViewCell.xib b/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleTableViewCell/TitleTableViewCell.xib index ddcc1fe09..f21d1db13 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleTableViewCell/TitleTableViewCell.xib +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/TitleTableViewCell/TitleTableViewCell.xib @@ -1,9 +1,9 @@ - + - + @@ -19,7 +19,7 @@ From df750d44b005ffa4cec2fce8831fc1a8b2ad6c20 Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Fri, 9 Jun 2023 16:25:37 +0400 Subject: [PATCH 057/126] add missed fonts for stack examples (stack screens all green) --- .../Stack/Views/View/UnrollCellStackView.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Example/ReactiveDataDisplayManager/Stack/Views/View/UnrollCellStackView.swift b/Example/ReactiveDataDisplayManager/Stack/Views/View/UnrollCellStackView.swift index f26cd61fd..85b5f635b 100644 --- a/Example/ReactiveDataDisplayManager/Stack/Views/View/UnrollCellStackView.swift +++ b/Example/ReactiveDataDisplayManager/Stack/Views/View/UnrollCellStackView.swift @@ -50,6 +50,8 @@ private extension UnrollCellStackView { let mainStack = UIStackView() mainStack.axis = .vertical mainStack.distribution = .fill + bigTextLabel.font = .preferredFont(forTextStyle: .body) + unrollButton.titleLabel?.font = .preferredFont(forTextStyle: .callout) unrollButton.setTitleColor(.blue, for: .normal) unrollButton.addTarget(self, action: #selector(unrollAction), for: .touchUpInside) mainStack.addArrangedSubview(bigTextLabel) From f593b4d037413d952404039196482da66f2f00c7 Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Fri, 9 Jun 2023 16:26:19 +0400 Subject: [PATCH 058/126] rename test utility method --- .../ReactiveDataDisplayManagerExampleUITests.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Example/ReactiveDataDisplayManagerExampleUITests/ReactiveDataDisplayManagerExampleUITests.swift b/Example/ReactiveDataDisplayManagerExampleUITests/ReactiveDataDisplayManagerExampleUITests.swift index ce43d6ea4..489a20242 100644 --- a/Example/ReactiveDataDisplayManagerExampleUITests/ReactiveDataDisplayManagerExampleUITests.swift +++ b/Example/ReactiveDataDisplayManagerExampleUITests/ReactiveDataDisplayManagerExampleUITests.swift @@ -16,17 +16,17 @@ class ReactiveDataDisplayManagerExampleUITests: BaseUITestCase { func testCollectionScreen() throws { setTab("Collection") - try assertAllScreensOpeningWithoutCrashes() + try assertAllScreensOpeningWithoutCrashesAndPassingAudit() } func testTableScreen() throws { setTab("Table") - try assertAllScreensOpeningWithoutCrashes() + try assertAllScreensOpeningWithoutCrashesAndPassingAudit() } func testStackScreen() throws { setTab("Stack") - try assertAllScreensOpeningWithoutCrashes() + try assertAllScreensOpeningWithoutCrashesAndPassingAudit() } } @@ -34,7 +34,7 @@ class ReactiveDataDisplayManagerExampleUITests: BaseUITestCase { private extension ReactiveDataDisplayManagerExampleUITests { - func assertAllScreensOpeningWithoutCrashes() throws { + func assertAllScreensOpeningWithoutCrashesAndPassingAudit() throws { let tablesQuery = app.tables for i in 0...tablesQuery.cells.count - 1 { print("===== cell number: \(i) =====") From f2b2f56a6a961b88a07a1cc6f2563b4e9ebb6522 Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Fri, 9 Jun 2023 16:57:09 +0400 Subject: [PATCH 059/126] add height calculation for table headers (table all green and nice visual) --- .../AlphabeticalTableViewController.swift | 2 +- .../ImageTableViewCell/ImageTableViewCell.xib | 4 ++-- .../Generators/SectionTitleFooterGenerator.swift | 8 +------- .../Generators/SectionTitleHeaderGenerator.swift | 8 +------- .../Headers/Generators/TitleHeaderGenerator.swift | 8 +------- .../Views/Headers/HeaderView/HeaderView.swift | 11 ++++++++++- .../Table/Views/Headers/HeaderView/HeaderView.xib | 14 +++++++------- 7 files changed, 23 insertions(+), 32 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Table/AlphabeticalTableViewController/AlphabeticalTableViewController.swift b/Example/ReactiveDataDisplayManager/Table/AlphabeticalTableViewController/AlphabeticalTableViewController.swift index 122a0b92b..7c37fc244 100644 --- a/Example/ReactiveDataDisplayManager/Table/AlphabeticalTableViewController/AlphabeticalTableViewController.swift +++ b/Example/ReactiveDataDisplayManager/Table/AlphabeticalTableViewController/AlphabeticalTableViewController.swift @@ -14,7 +14,7 @@ final class AlphabeticalTableViewController: UIViewController { // MARK: - Constants private enum Constants { - static let alphabets = ["A", "B", "C", "D"] + static let alphabets = ["AAA", "BBB", "CCC", "DDD"] static let models = [String](repeating: "Cell", count: 5) } diff --git a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.xib b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.xib index a2bab5de3..6dff1f9e2 100644 --- a/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.xib +++ b/Example/ReactiveDataDisplayManager/Table/Views/Cells/ImageTableViewCell/ImageTableViewCell.xib @@ -17,8 +17,8 @@ - From ba6aa1854a475679bd88c3a8286abf2c3f2b0cc3 Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Tue, 13 Jun 2023 15:44:26 +0400 Subject: [PATCH 065/126] make compositional grid layout sizable (from 9 to 6 issues) --- ...ollectionCompositionalViewController.swift | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Collection/CollectionCompositionalViewController/CollectionCompositionalViewController.swift b/Example/ReactiveDataDisplayManager/Collection/CollectionCompositionalViewController/CollectionCompositionalViewController.swift index 52aca6fac..0fc022ce1 100644 --- a/Example/ReactiveDataDisplayManager/Collection/CollectionCompositionalViewController/CollectionCompositionalViewController.swift +++ b/Example/ReactiveDataDisplayManager/Collection/CollectionCompositionalViewController/CollectionCompositionalViewController.swift @@ -191,7 +191,19 @@ private extension CollectionCompositionalViewController { let footer = makeSectionFooter() // Item - let item = makeItem(with: .init(width: 0.33, height: 1.0)) + let item: NSCollectionLayoutItem = { + if #available(iOS 17.0, *) { + let item = makeItem(with: makeAutoLayoutSize(for: .init(width: 128, height: 128))) + item.edgeSpacing = NSCollectionLayoutEdgeSpacing(leading: .fixed(12), + top: .fixed(12), + trailing: .fixed(12), + bottom: .fixed(12)) + return item + } else { + return makeItem(with: makeLayoutSize(for: .init(width: 0.33, height: 1))) + } + + }() // Group let group = NSCollectionLayoutGroup.horizontal(layoutSize: makeLayoutSize(for: .init(width: 1.0, height: 0.2)), subitems: [item]) @@ -211,10 +223,10 @@ private extension CollectionCompositionalViewController { let footer = makeSectionFooter() // Item medium image - let leadingItem = makeItem(with: .init(width: 0.7, height: 1.0)) + let leadingItem = makeItem(with: makeLayoutSize(for: .init(width: 0.7, height: 1.0))) // Item small image - let trailingItem = makeItem(with: .init(width: 1.0, height: 0.3)) + let trailingItem = makeItem(with: makeLayoutSize(for: .init(width: 1.0, height: 0.3))) // Group combine 2 small image let trailingGroup = NSCollectionLayoutGroup.vertical(layoutSize: makeLayoutSize(for: .init(width: 0.3, height: 1.0)), @@ -226,7 +238,7 @@ private extension CollectionCompositionalViewController { subitems: [leadingItem, trailingGroup]) // Item long image) - let topItem = makeItem(with: .init(width: 1.0, height: 0.3)) + let topItem = makeItem(with: makeLayoutSize(for: .init(width: 1.0, height: 0.3))) // Main Group long image / medium image | 2 small image let nestedGroup = NSCollectionLayoutGroup.vertical(layoutSize: makeLayoutSize(for: .init(width: 1.0, height: 0.4)), @@ -251,8 +263,7 @@ private extension CollectionCompositionalViewController { alignment: .bottom) } - func makeItem(with size: CGSize, contentInsets: NSDirectionalEdgeInsets = Constants.edgeInsets) -> NSCollectionLayoutItem { - let layoutSize = makeLayoutSize(for: size) + func makeItem(with layoutSize: NSCollectionLayoutSize, contentInsets: NSDirectionalEdgeInsets = Constants.edgeInsets) -> NSCollectionLayoutItem { let item = NSCollectionLayoutItem(layoutSize: layoutSize) item.contentInsets = contentInsets return item @@ -263,4 +274,10 @@ private extension CollectionCompositionalViewController { heightDimension: .fractionalHeight(size.height)) } + @available(iOS 17.0, *) + func makeAutoLayoutSize(for estimatedSize: CGSize) -> NSCollectionLayoutSize { + return NSCollectionLayoutSize(widthDimension: .uniformAcrossSiblings(estimate: estimatedSize.width), + heightDimension: .estimated(estimatedSize.height)) + } + } From c60d0c713d0173dd447829710385a2b2aea162ea Mon Sep 17 00:00:00 2001 From: Nikita Korobeinikov Date: Tue, 13 Jun 2023 17:46:30 +0400 Subject: [PATCH 066/126] fix issue in sizable collection example (all green) --- .../VerticalSizableTextGenerator.swift | 3 ++- .../SizableCollectionViewCell.swift | 26 +++++++++---------- .../SizableCollectionViewCell.xib | 2 +- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/Generators/VerticalSizableTextGenerator.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/Generators/VerticalSizableTextGenerator.swift index 8fc0fae61..160b013ec 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/Generators/VerticalSizableTextGenerator.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/Generators/VerticalSizableTextGenerator.swift @@ -31,7 +31,8 @@ final class VerticalSizableTextGenerator: BaseCollectionCellGenerator CGSize { - return SizableCollectionViewCell.getCellSize(for: text, withWight: maxWight) + .init(width: maxWight, + height: SizableCollectionViewCell.getHeight(forWidth: maxWight, with: text)) } } diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.swift b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.swift index f46a7f169..37d287375 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.swift +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.swift @@ -11,12 +11,6 @@ import ReactiveDataDisplayManager final class SizableCollectionViewCell: UICollectionViewCell { - // MARK: - Constants - - private enum Constants { - static var titleFont = UIFont.preferredFont(forTextStyle: .subheadline) - } - // MARK: - IBOutlet @IBOutlet private weak var titleLabel: UILabel! @@ -28,13 +22,6 @@ final class SizableCollectionViewCell: UICollectionViewCell { setupInitialState() } - // MARK: - Static Methods - - static func getCellSize(for viewModel: String, withWight wight: CGFloat) -> CGSize { - let height = viewModel.getHeight(withConstrainedWidth: wight, font: Constants.titleFont) - return CGSize(width: wight, height: height) - } - } // MARK: - ConfigurableItem @@ -47,6 +34,17 @@ extension SizableCollectionViewCell: ConfigurableItem { } +// MARK: - CalculatableHeightItem + +extension SizableCollectionViewCell: CalculatableHeightItem { + + static func getHeight(forWidth width: CGFloat, with model: String) -> CGFloat { + model.getHeight(withConstrainedWidth: width, + font: .preferredFont(forTextStyle: .subheadline)) + } + +} + // MARK: - AccessibilityItem extension SizableCollectionViewCell: AccessibilityItem { @@ -63,7 +61,7 @@ private extension SizableCollectionViewCell { func setupInitialState() { // configure titleLabel titleLabel.numberOfLines = 0 - titleLabel.font = Constants.titleFont + titleLabel.font = .preferredFont(forTextStyle: .subheadline) } } diff --git a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.xib b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.xib index 5290ca9c5..28420237b 100644 --- a/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.xib +++ b/Example/ReactiveDataDisplayManager/Collection/Views/Cells/SizableCollectionViewCell/SizableCollectionViewCell.xib @@ -17,7 +17,7 @@ -