Setting accessibility focus when switching tabs

I'm trying to make macOS VoiceOver read some in text in the parent tabview when the child tabview changes tabs. VoiceOver always reads the first text entry in the child sub tab ignoring attempts to switch where the focus is.

I've tried these things, in the example textItem is a member of the parent tabview class:

<parent tabview>.setAccessibilityApplicationFocusedUIElement(textItem) <textItem>.setAccessibilityFocused(true)

Each sub tab is a view controller loaded from a storyboard and I've added code in viewDidAppear to set the accessibility focus. I've also tried using a notification to the parent tab view to set the accessibility focus at the end of the sub tab's viewDidAppear.

Nothing seems to work, is there way to actually change the current focused accessibility UI element programmatically? This needs to work on macOS 13 and greater.

Here is a rough layout of what I'm trying to accomplish. When the use selects "sub tab 2", I want the text "Text to read first" to be the focus and have VoiceOver read that. What really happens is VoiceOver reads the contents of the sub tab "Feature Name"

I assume VoiceOver cursor moved to "Feature Name" and described it because of some notification, such as AXFocusedTabChangedNotification. When you move from sub tab 1 to sub tab 2, having VO cursor move to the first item in the new tab view is what user would expect. User would actually appear disoriented if the VO cursor jumped to a different location like the "text to read first" element. If the purpose is purely for VO user to hear some additional information when you switch tab, then AXAnnouncementRequestedNotification would be the better choice, while still retaining the ability for VO cursor to naturally go to the first item in the new tab view.

You are correct, I'm trying to read some additional information before reading the UI on the new tab. A concrete example is in System Settings, when you select an item on the left side (General), read the title (General) on the right side before reading the feature list (About).

Using your suggestion, I tried adding the following code to the viewDidLoad, viewWillAppear functions of the sub tab, nothing was read. I added the code to the main tab handler when the sub tab was clicked and nothing happens.

"Text to read first" is a NSTextField

Tried both:

        NSAccessibility.post(element: textField as Any, notification: .announcementRequested)

and

        NSAccessibility.post(element: textField as Any, notification: .announcementRequested, userInfo: [.priority: NSAccessibilityPriorityLevel.high])

VoiceOver is active and reading the screen but the announcement is never read and the "Feature Name" element is the first thing read when switching to the sub tab.

Am I using the wrong approach?

When should I post the notification?

Is announcementRequested handled by VoiceOver?

Thanks!

Found some more information about .announcementRequested and I wasn't passing the string to read, so I hoped changing the code to the following would work, but it didn't, nothing is read except for the button "Feature Name"

NSAccessibility.post(element: tabVC.textField as Any,
                             notification: .announcementRequested,
                             userInfo: [.announcement: textField.stringValue, .priority: NSAccessibilityPriorityLevel.high])

I've tried putting the above code in the parent tab view controller before the sub tab is displayed, no help. Moved the announcement code to viewDidLoad and viewWillAppear and neither worked.

Is it really possible to achieve this behavior?

The problem I initially asked about is resolved but only for macOS 14 and higher, whereas I needed macOS 13 support.

If anyone else has this issue, the key was making the call in the tab view controller before switching to the sub tab view. The announcement is made and "Text to read first" is read and the "Feature Name" is read but only on macOS 14+.

I tried various UI elements for the the NSAccessibility.post() call, the sub tab view controller, the main tab view controller, the view containing both, the text field itself, and finally the main window, none resulted in the correct behavior.

        if #available(macOS 14.0, *) {
            AccessibilityNotification.ScreenChanged(self).post()
            AccessibilityNotification.Announcement(textField.stringValue).post()
        }
        else {
            // Fallback on earlier versions
            NSAccessibility.post(element: NSApp.mainWindow as Any,
                                 notification: .announcementRequested,
                                 userInfo: [.announcement: textField.stringValue, .priority: NSAccessibilityPriorityLevel.high])
            
            NSAccessibility.post(element: textField as Any, notification: .titleChanged)
        }

Setting accessibility focus when switching tabs
 
 
Q