开启VoiceOver时,如何点击蒙层关闭UIPresentController及UIPresentViewController?
Hey there! Let’s break down these two VoiceOver-related modal view controller issues—they’re super common when building accessible iOS apps, so I’ve got practical fixes for both.
1. 点击蒙层关闭自定义弹窗(UIPresentationController 场景)
When you use a custom UIPresentationController to show a modal with a dimmer/overlay view, the tap-to-dismiss works fine for sighted users, but VoiceOver won’t recognize the overlay as an interactive element by default. Here’s how to fix it:
Step 1: Make the overlay accessible
Configure the dimmer view so VoiceOver knows it’s a tappable button:let dimmingView = UIView() dimmingView.backgroundColor = UIColor.black.withAlphaComponent(0.5) // Accessibility setup dimmingView.isAccessibilityElement = true dimmingView.accessibilityLabel = NSLocalizedString("关闭弹窗", comment: "Accessibility label for dimmer view") dimmingView.accessibilityTraits = .button // Tells VoiceOver it's a tappable buttonStep 2: Add tap handling (works for both touch and VoiceOver)
Add a tap gesture recognizer to trigger dismissal. VoiceOver’s double-tap will automatically activate this gesture once the overlay is marked as accessible:let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissModal)) dimmingView.addGestureRecognizer(tapGesture) @objc private func dismissModal() { // Dismiss the presented view controller presentingViewController?.dismiss(animated: true, completion: nil) }Pro Tip: In your custom
UIPresentationController, make sure to add the dimming view to the container view inpresentationTransitionWillBegin()so it sits behind your modal content but covers the presenting view.
2. 正确关闭 UIPresentedViewController(VoiceOver 适配)
If you’re struggling with dismissing a standard presented view controller (not a custom one) when VoiceOver is on, the key is ensuring all interactive dismissal triggers are accessible:
Case 1: Using a close button
Make sure your close button has clear accessibility labels and traits so VoiceOver users can find and activate it:let closeButton = UIButton(type: .system) closeButton.setTitle("关闭", for: .normal) closeButton.accessibilityLabel = NSLocalizedString("关闭弹窗", comment: "Accessibility label for close button") closeButton.accessibilityTraits = .button closeButton.addTarget(self, action: #selector(dismissModal), for: .touchUpInside)Case 2: Swipe-to-dismiss (sheet-style modals)
iOS natively supports swipe-to-dismiss for sheet modals even with VoiceOver enabled, but if you’ve customized the modal presentation style, ensure you haven’t broken this behavior. If you have a custom transition, add accessibility support for the swipe gesture by posting a screen change notification once dismissal completes:@objc private func dismissModal() { dismiss(animated: true) { // Notify VoiceOver that the screen has changed back to the presenting view UIAccessibility.post(notification: .screenChanged, argument: nil) } }Pro Tip: When your modal first appears, direct VoiceOver focus to the first important interactive element (like a text field or primary button) to guide users:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) UIAccessibility.post(notification: .screenChanged, argument: yourFirstInteractiveElement) }
内容的提问来源于stack exchange,提问作者lypw2009

