iosswiftuiaccessibility

Accessibility set focus to navigation bar title item


Overview:

I would like to set the accessibility focus to the navigation bar's title item.

By default the focus is set from top left, meaning the back button would be on focus.

I would like the title item to be in focus.

Attempts made so far:

    UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification,
                                    navigationController?.navigationBar.items?.last)

Problem:

Possible Cause:


Solution

  • Solution 1 I don't like it, but it was the minimum amount of hacking that does not rely on digging through hidden subviews (internal implementation of UINavigationBar view hierarchy).

    First in viewWillAppear, I store a backup reference of the back button item, and then remove the back button item (leftBarButtonItem):

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    
        backButtonBackup = self.navigationItem.leftBarButtonItem
        self.navigationItem.leftBarButtonItem = nil  
    }
    

    Then I restore the back item, but only after I dispatch the screen changed event in viewDidAppear() :

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    
        UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
            self?.navigationItem.leftBarButtonItem = self?.backButtonBackup
        }
    }
    

    Solution 2: Disable all accessibility on the nav bar and view controller up until viewDidAppear() is finished:

        self.navigationController.navigationBar.accessibilityElementsHidden = true
        self.view.accessibilityElementsHidden = true
    

    , and then in viewDidAppear manually dispatching the layout element accessibility focused event to the label subview of UINavigationBar:

    UIAccessibilityPostNotification( UIAccessibilityLayoutChangedNotification, self.navigationController.navigationBar.subviews[2].subviews[1])   
    //  The label buried inside the nav bar.   Not tested on all iOS versions.  
    // Alternately you can go digging for the label by checking class types. 
    // Then use DispatchAsync, to re-enable accessibility on the view and nav bar again... 
    

    I'm not a fan of this method either.

    DispatchAsync delay in viewDidAppear seems to be needed in any case - and I think both solutions are still horrible.