Reputation: 991
OVERVIEW I'm having trouble getting correct focus order (Accessibility in iOS). It seems like becomeFirstResponder() overwrites my focus order I have specified in the array and causes Voice Over Accessibility functionality to read wrong Accessibility Label first.
DETAILS: I have a View Controller with containerView. Inside I have UIView of my progress bar image and text input field (placeholder). Both elements have isAccessibilityElement = true attributes and they have been added to my focus order array. However upon screen launch, focus order goes to the input field instead of progress bar UIView.
After extended testing I've noticed that this issue is no longer replicable if I remove below line of code.
otpNumberTextField.becomeFirstResponder()
But this is not a solution. I need cursor in the textfield but Voice Over functionality to read my Progress Bar Accessibility Label first. How to fix it?
SPECIFIC SCENARIO I've noticed this issue occurs only when I have VC with a last active focus on a Textfield and then transition to the next VC (with a Textfield and a Progress Bar).
Bug is not replicable when I have VC with a last active focus on the Button and then transition to the next VC (with a Textfield and a Progress Bar).
CODE SNIPPET
import UIKit
class MyViewController: UIViewController, UITextFieldDelegate {
var otpNumberTextField = UITextField()
var progressMainDot = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
setupView()
setupBinding()
}
override func viewWillAppear(_ animated: Bool) {
setupView()
textFieldDidChange(otpNumberTextField)
}
func setupView(){
let containerView = UIView()
containerView.backgroundColor = UIColor.init(named: ColourUtility.BackgroundColour)
view.addSubview(containerView)
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
containerView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
//Progress Bar
let progressBarView = UIView()
containerView.addSubview(progressBarView)
progressBarView.isAccessibilityElement = true
progressBarView.accessibilityLabel = "my accessibility label"
progressBarView.translatesAutoresizingMaskIntoConstraints = false
progressMainDot.image = UIImage(named:ImageUtility.progressMain)
progressMainDot.contentMode = .scaleAspectFit
progressBarView.addSubview(progressMainDot)
//Text Field
otpNumberTextField.borderStyle = UITextField.BorderStyle.none
otpNumberTextField.font = UIFontMetrics.default.scaledFont(for: FontUtility.inputLargeTextFieldStyle)
otpNumberTextField.adjustsFontForContentSizeCategory = true
otpNumberTextField.isAccessibilityElement = true
otpNumberTextField.accessibilityLabel = AccessibilityUtility.enterVerificationCode
otpNumberTextField.placeholder = StringUtility.otpPlaceholder
otpNumberTextField.textColor = UIColor.init(named: ColourUtility.TextfieldColour)
otpNumberTextField.textAlignment = .center
otpNumberTextField.keyboardType = .numberPad
otpNumberTextField.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: .editingChanged)
containerView.addSubview(otpNumberTextField)
otpNumberTextField.becomeFirstResponder()
//Accessibility - focus order
view.accessibilityElements = [progressBarView, otpNumberTextField]
}
//... more code goes here ...
}
Upvotes: 1
Views: 6949
Reputation: 71
If you have already set accessibilityElements
, then voice over should respects that order but calling becomeFirstResponder()
changes the focus to that text field.
You can try below code, which notifies voice over for shifting the focus to new element due to layout changes.
UIAccessibility.post(notification: .layoutChanged, argument: progressBarView)
So now your modified method should be like below:
func setupView(){
.....
otpNumberTextField.becomeFirstResponder()
//Accessibility - focus order
view.accessibilityElements = [progressBarView, otpNumberTextField]
UIAccessibility.post(notification: .layoutChanged, argument: progressBarView)
.....
}
Upvotes: 2