How group a UITableView for VoiceOver?

I have a UITableView with infinite cells, these are loaded as you scroll down. And I also have a floating button at the bottom. VoiceOver reads the cells one by one, the problem is that as new cells are loaded, the button is never reached. I would like VoiceOver to treat the table as a group, to access and go through its elements and to be able to exit to go to the button. What I want is something similar to what the mail app currently does.

I'm working with UIKit in Swift.

I've tried setting the UITableView with the isAccessibilityElement flag to true but it doesn't seem to affect anything. I have also tried with the accessibilityContainerType setting it to .semanticGroup.

The accessibilityLabel to the UITableView does read it when it enters the first cell.

Upvotes: 0

Views: 423

Answers (1)

XLE_22
XLE_22

Reputation: 5671

You followed the appropriate path by using the accessibilityContainerType property with the .semanticGroup value but I think you wrote it only for the table view. šŸ˜³
To move from one group to another, this property should be inserted to each of the desired elements.

I reproduced your environment under Xcode 14 and iOS 16.4 using UIKit and the storyboard. enter image description here

I implemented this property to the label, the table view and the button so as to make VoiceOver understand that three different groups of elements may be selected:

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var myLabel: UILabel!
    @IBOutlet weak var myTableView: UITableView!
    @IBOutlet weak var myButton: UIButton!
    let maxCells = 100

    override func viewDidLoad() {
        super.viewDidLoad()
    
        myLabel.accessibilityContainerType = .semanticGroup
        myTableView.accessibilityContainerType = .semanticGroup
        myButton.accessibilityContainerType = .semanticGroup
    
        myTableView.delegate = self
        myTableView.dataSource = self
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    
        myTableView.headerView(forSection: 0)?.accessibilityFrame = myTableView.frame
        myTableView.headerView(forSection: 0)?.accessibilityLabel = "HERE IS THE TABLE VIEW"
    }

    func tableView(_ tableView: UITableView,
                   numberOfRowsInSection section: Int) -> Int { return maxCells }

    func tableView(_ tableView: UITableView,
                   titleForHeaderInSection section: Int) -> String? { return "THE TABLE VIEW TITLE" }

    func tableView(_ tableView: UITableView,
                   cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
        let myCell = myTableView.dequeueReusableCell(withIdentifier: "myCell",
                                                     for: indexPath)
        var config = myCell.defaultContentConfiguration()
        config.text = "Table view cell nĀ° \(indexPath.row)"
    
        myCell.contentConfiguration = config
        return myCell
    }
}

Next, follow the steps below to understand the proper gestures for reaching your goal with VoiceOver:

  1. Through the rotor, select the Containers item.
  2. From the label, flick down using one finger to reach the first element in the table view.
  3. From this location, reproduce this gesture to get out of the table view and find the button. enter image description here
  4. When inside the table view, it's still possible to scroll for getting each cells by swiping left/right with one finger.
  5. Wherever the location in the table view, you can reach the button just by flicking down using one finger. enter image description here

You can now consider your table view as a group and get out of it without scrolling all the cells inside with VoiceOver. šŸ˜‰šŸŖšŸŽ‰

āš ļø EDIT 2023/09/04 āš ļø

I've finally understood the way you got this use case. šŸ„³
To get this specific behavior, you use the Navigation Style mode available in the settings (through the rotor as well) since iOS 15: enter image description here

When developing, this API is only available for the Switch Control and can be embedded in an app this way but it's impossible with VoiceOver for which Apple uses this property as private.

Switch Control uses this technology, but VoiceOver and other assistive technologies do not.

Moreover, there's no notification to be aware of the Navigation Style mode. šŸ˜“

Finally, if you want to reach a behavior like the native mail app with VoiceOver in iOS 16 with UIKit, the only way is to use the containers as described in my initial answer. šŸ˜‰

Upvotes: 2

Related Questions