Порядок фокусировки на доступности не работает должным образом [iOS]

#ios #swift #accessibility #voiceover #becomefirstresponder

#iOS #быстрый #Специальные возможности #голос за кадром #станьте первым ответчиком #swift

Вопрос:

ОБЗОР У меня возникли проблемы с получением правильного порядка фокусировки (доступность в iOS). Похоже, что becomeFirstResponder() перезаписывает мой порядок фокусировки, который я указал в массиве, и заставляет функцию голосового доступа сначала считывать неправильную метку доступности.

ПОДРОБНОСТИ: у меня есть контроллер представления с containerView. Внутри у меня есть UIView моего изображения индикатора выполнения и поля ввода текста (заполнителя). Оба элемента имеют атрибуты isAccessibilityElement = true, и они были добавлены в мой массив порядка фокусировки. Однако при запуске экрана порядок фокусировки переходит в поле ввода вместо индикатора выполнения UIView.

После расширенного тестирования я заметил, что эта проблема больше не воспроизводится, если я удалю нижеприведенную строку кода.

  otpNumberTextField.becomeFirstResponder()
  

Но это не решение. Мне нужен курсор в текстовом поле, но функция озвучивания, чтобы сначала прочитать метку доступности индикатора выполнения. Как это исправить?

КОНКРЕТНЫЙ СЦЕНАРИЙ Я заметил, что эта проблема возникает только тогда, когда у меня есть VC с последним активным фокусом на текстовом поле, а затем переход к следующему VC (с текстовым полем и индикатором выполнения).

Ошибка не воспроизводится, когда у меня есть VC с последним активным фокусом на кнопке, а затем переход к следующему VC (с текстовым полем и индикатором выполнения).

ФРАГМЕНТ КОДА

 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 ...

}
  

Ответ №1:

Если вы уже установили accessibilityElements , то voice over должен соблюдать этот порядок, но вызов becomeFirstResponder() изменяет фокус на это текстовое поле.

Вы можете попробовать приведенный ниже код, который уведомляет голос за кадром о переносе фокуса на новый элемент из-за изменений макета.

 UIAccessibility.post(notification: .layoutChanged, argument: progressBarView)
  

Итак, теперь ваш модифицированный метод должен быть таким, как показано ниже:

 func setupView(){
    .....
    otpNumberTextField.becomeFirstResponder()

    //Accessibility - focus order
    view.accessibilityElements = [progressBarView, otpNumberTextField]
            
    UIAccessibility.post(notification: .layoutChanged, argument: progressBarView)

    .....
}
  

Комментарии:

1. Ом Пракаш Я пробовал это раньше, но решение, похоже, глючит. Когда я использовал приведенный ниже код, VoiceOver по-прежнему сначала начинает считывать inputTextField и только через некоторое время переключается и начинает считывать мой индикатор выполнения. В настоящее время у меня есть обходной путь, позволяющий скрыть текстовое поле от голоса за кадром ‘otpNumberTextField.accessibilityElementsHidden = true’, а затем использовать функцию, чтобы отобразить его через 5 секунд. Таким образом, VoiceOver сначала начнет считывать индикатор выполнения. Также обратите внимание, что эта ошибка возникает только тогда, когда предыдущий VC был сфокусирован на другом поле ввода (не возникает для VC, последний активный фокус которого был на кнопке).