Skip to content

Commit a37dd79

Browse files
authored
[Stats Revamp] VoiceOver improvements (#19773)
* Make Add Stats Card button visible for voice over in No Results View * Voice inactive card hint for a row in Inactive cards section * Reselect row for voiceover when card is moved from active to inactive After we remove a row from section, iOS automatically selects another row that is reused for voiceover and automatically reads it. Using UIAccessibility.post API to manually select a row that was moved to another section. * Enable ability to reload ImmuTable manually * Manually move rows when toggling rows to support animation and voiceover * Fix typo * Reload the data of the cell after moving the row to update accessibility data
1 parent d698353 commit a37dd79

File tree

3 files changed

+76
-10
lines changed

3 files changed

+76
-10
lines changed

WordPress/Classes/Utility/ImmuTable.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ open class ImmuTableViewHandler: NSObject, UITableViewDataSource, UITableViewDel
256256
/// An ImmuTable object representing the table structure.
257257
open var viewModel = ImmuTable.Empty {
258258
didSet {
259-
if target.isViewLoaded {
259+
if target.isViewLoaded && automaticallyReloadTableView {
260260
target.tableView.reloadData()
261261
}
262262
}
@@ -265,6 +265,9 @@ open class ImmuTableViewHandler: NSObject, UITableViewDataSource, UITableViewDel
265265
/// Configure the handler to automatically deselect any cell after tapping it.
266266
@objc var automaticallyDeselectCells = false
267267

268+
/// Automatically reload table view when view model changes
269+
@objc var automaticallyReloadTableView = true
270+
268271
// MARK: UITableViewDataSource
269272

270273
open func numberOfSections(in tableView: UITableView) -> Int {

WordPress/Classes/ViewRelated/Stats/Insights/Insights Management/InsightsManagementViewController.swift

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ class InsightsManagementViewController: UITableViewController {
2121
}
2222
}
2323

24+
private var insightsInactive: [StatSection] {
25+
InsightsManagementViewController.allInsights.filter({ !self.insightsShown.contains($0) })
26+
}
27+
2428
private var hasChanges: Bool {
2529
return insightsShown != originalInsightsShown
2630
}
@@ -30,7 +34,9 @@ class InsightsManagementViewController: UITableViewController {
3034
private lazy var saveButton = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(saveTapped))
3135

3236
private lazy var tableHandler: ImmuTableViewHandler = {
33-
return ImmuTableViewHandler(takeOver: self)
37+
let handler = ImmuTableViewHandler(takeOver: self)
38+
handler.automaticallyReloadTableView = false
39+
return handler
3440
}()
3541

3642
// MARK: - Init
@@ -214,6 +220,7 @@ private extension InsightsManagementViewController {
214220

215221
func reloadViewModel() {
216222
tableHandler.viewModel = tableViewModel()
223+
tableView.reloadData()
217224
}
218225

219226
func tableViewModel() -> ImmuTable {
@@ -250,7 +257,7 @@ private extension InsightsManagementViewController {
250257
}
251258

252259
func inactiveCardsSection() -> ImmuTableSection {
253-
let rows = InsightsManagementViewController.allInsights.filter({ !self.insightsShown.contains($0) })
260+
let rows = insightsInactive
254261

255262
guard rows.count > 0 else {
256263
return ImmuTableSection(headerText: TextContent.inactiveCardsHeader, rows: [inactivePlaceholderRow])
@@ -259,7 +266,7 @@ private extension InsightsManagementViewController {
259266
return ImmuTableSection(headerText: TextContent.inactiveCardsHeader,
260267
rows: rows.map {
261268
return AddInsightStatRow(title: $0.insightManagementTitle,
262-
enabled: true,
269+
enabled: false,
263270
action: rowActionFor($0)) }
264271
)
265272
}
@@ -305,11 +312,67 @@ private extension InsightsManagementViewController {
305312
func toggleRow(for statSection: StatSection) {
306313
if let index = insightsShown.firstIndex(of: statSection) {
307314
insightsShown.remove(at: index)
308-
} else {
315+
moveRowToInactive(at: index, statSection: statSection)
316+
} else if let inactiveIndex = insightsInactive.firstIndex(of: statSection) {
309317
insightsShown.append(statSection)
318+
moveRowToActive(at: inactiveIndex, statSection: statSection)
310319
}
320+
}
311321

312-
reloadViewModel()
322+
// Animates the movement of a row from the inactive to active section, supports accessibility
323+
func moveRowToActive(at index: Int, statSection: StatSection) {
324+
tableHandler.viewModel = tableViewModel()
325+
326+
let origin = IndexPath(row: index, section: 1)
327+
let row = insightsShown.firstIndex(of: statSection) ?? (insightsShown.count - 1)
328+
let destination = IndexPath(row: row, section: 0)
329+
330+
tableView.performBatchUpdates {
331+
tableView.moveRow(at: origin, to: destination)
332+
333+
/// Account for placeholder cell addition to inactive section
334+
if insightsInactive.isEmpty {
335+
tableView.insertRows(at: [.init(row: 0, section: 1)], with: .none)
336+
}
337+
338+
/// Account for placeholder cell removal from active section
339+
if insightsShown.count == 1 {
340+
tableView.deleteRows(at: [.init(row: 0, section: 0)], with: .automatic)
341+
}
342+
}
343+
344+
/// Reload the data of the row to update the accessibility information
345+
if let cell = tableView.cellForRow(at: destination), insightsShown.count > 0 {
346+
tableHandler.viewModel.rowAtIndexPath(destination).configureCell(cell)
347+
}
348+
}
349+
350+
// Animates the movement of a row from the active to inactive section, supports accessibility
351+
func moveRowToInactive(at index: Int, statSection: StatSection) {
352+
tableHandler.viewModel = tableViewModel()
353+
354+
let origin = IndexPath(row: index, section: 0)
355+
let row = insightsInactive.firstIndex(of: statSection) ?? 0
356+
let destination = IndexPath(row: row, section: 1)
357+
358+
tableView.performBatchUpdates {
359+
tableView.moveRow(at: origin, to: destination)
360+
361+
/// Account for placeholder cell addition to active section
362+
if insightsShown.isEmpty {
363+
tableView.insertRows(at: [.init(row: 0, section: 0)], with: .none)
364+
}
365+
366+
/// Account for placeholder cell removal from inactive section
367+
if insightsInactive.count == 1 {
368+
tableView.deleteRows(at: [.init(row: 0, section: 1)], with: .automatic)
369+
}
370+
}
371+
372+
/// Reload the data of the row to update the accessibility information
373+
if let cell = tableView.cellForRow(at: destination), insightsInactive.count > 0 {
374+
tableHandler.viewModel.rowAtIndexPath(destination).configureCell(cell)
375+
}
313376
}
314377

315378
var placeholderRow: ImmuTableRow {

WordPress/Classes/ViewRelated/Views/NoResultsViewController.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -588,11 +588,11 @@ private extension NoResultsViewController {
588588
view.accessibilityLabel = titleLabel.text
589589
view.accessibilityTraits = .staticText
590590
} else {
591-
view.accessibilityElements = [noResultsView!, actionButton!]
591+
view.accessibilityElements = [labelStackView!, actionButton!]
592592

593-
noResultsView.isAccessibilityElement = true
594-
noResultsView.accessibilityTraits = .staticText
595-
noResultsView.accessibilityLabel = [
593+
labelStackView.accessibilityTraits = .staticText
594+
labelStackView.isAccessibilityElement = true
595+
labelStackView.accessibilityLabel = [
596596
titleLabel.text,
597597
subtitleTextView.isHidden ? nil : subtitleTextView.attributedText.string
598598
].compactMap { $0 }.joined(separator: ". ")

0 commit comments

Comments
 (0)