How to disable main UI when overlay appears?

1.2k Views Asked by At

I've built a streaming audio app that looks like this:

enter image description here

When the stream is lost, I overlay this screen:

enter image description here

My issue is that with Voiceover ON, all the underlying controls are still active: I can swipe to them all, and adjust their value.

Here is a snippet of my code for when the signal is lost and regained:

@objc func lostStream() {
    DispatchQueue.main.async {
        self.lossOfSignalBlocker.alpha = 0.0
        self.lossOfSignalBlocker.frame = self.view.bounds
        self.view.addSubview(self.lossOfSignalBlocker)
        self.lossOfSignalBlocker.isUserInteractionEnabled = true
        //UIView.animate(withDuration: 0.2) { self.lossOfSignalBlocker.alpha = 1.0 }
        UIView.animate( withDuration: 0.2, animations: { self.lossOfSignalBlocker.alpha = 1.0 } )

        //Announce loss of signal to Voiceover user.
        UIAccessibilityPostNotification(
            UIAccessibilityAnnouncementNotification,
            "Signal Lost" as NSString
        )
    }
}

@objc func regainedStream() {
    DispatchQueue.main.async {
        UIView.animate( withDuration: 0.2, animations: { self.lossOfSignalBlocker.alpha = 0.0 } )
        { _ in
            self.lossOfSignalBlocker.removeFromSuperview()
        }
    }
}

How do I disable the main UI so that only the overlay responds to any Voiceover-related actions?

2

There are 2 best solutions below

0
Manelion On BEST ANSWER

OK! I've made it work. In addition to setting 'accessibilityElementsHidden' to TRUE, you have to tell the app that the screen has changed by calling 'UIAccessibilityPostNotification' with the 'UIAccessibilityScreenChangedNotification' notification.

Here's what that code looks like now:

@objc func lostStream() {
    DispatchQueue.main.async {

        self.lossOfSignalBlocker.alpha = 0.0
        self.lossOfSignalBlocker.frame = self.view.bounds
        self.view.addSubview(self.lossOfSignalBlocker)
        self.lossOfSignalBlocker.isUserInteractionEnabled = true
        UIView.animate( withDuration: 0.1, animations: { self.lossOfSignalBlocker.alpha = 1.0 } )

        //Disable Voiceover accessibility controls in main view
        self.tableView.accessibilityElementsHidden = true

        //Notify app the screen has changed.
        UIAccessibilityPostNotification(
            UIAccessibilityScreenChangedNotification,
            nil
        )

        //Announce loss of signal to Voiceover user.
        UIAccessibilityPostNotification(
            UIAccessibilityAnnouncementNotification,
            "Signal Lost. Reconnecting." as NSString
        )

    }
}

@objc func regainedStream() {
    DispatchQueue.main.async {

        UIView.animate( withDuration: 0.2, animations: { self.lossOfSignalBlocker.alpha = 0.0 } )
        { _ in
            self.lossOfSignalBlocker.removeFromSuperview()
        }

        //Re-enable Voiceover accessibility controls in main view
        self.tableView.accessibilityElementsHidden = false

        //Notify app the screen has changed.
        UIAccessibilityPostNotification(
            UIAccessibilityScreenChangedNotification,
            nil
        )

        //Announce signal regained to Voiceover user.
        UIAccessibilityPostNotification(
            UIAccessibilityAnnouncementNotification,
            "Reconnected." as NSString
        )

    }
}
1
Sam On

On UIAccessibility elements (eg. a view), there is a property called accessibilityElementsHidden. Setting this value to true should hide views that are covered by the arrival of a the "reconnecting" view.

Considering setting the this value to true on the UITableView when in the background.

For more details check out the documentation: https://developer.apple.com/documentation/objectivec/nsobject/1615080-accessibilityelementshidden