好。 This answer很有帮助。显示屏幕时,我可以选择一个可访问性项目。我只是添加

UIAccessibility.post(notification: .layoutChanged, argument: <a reference to the UI item to receive focus>)

到我的viewWillAppear()方法的末尾,该项目将获得焦点。

但是,在我的一个屏幕中,我要获得焦点的项目是一个UISegmentedControl,并且在聚焦后,无论选择哪个项目,它始终会选择第一个项目。由于遵循了极好的建议here,因此控件中的每个项目都有一个可访问性标签,并且我希望我的重点从所选的任何细分开始。

有没有办法做到这一点?通常,我会尽量避免使用“hacky”解决方案(就像我刚才提到的那样),但是我愿意考虑任何事情。

谢谢!

更新:只是为了增加侮辱性伤害,我还希望选择要选择的项目时遇到问题,然后过一秒钟,屏幕将选择内容跳到了第一个项目。这可能是第二个问题的话题。

最佳答案

我创建了一个空白项目,如下所示以重现该问题:
ios - 如何将可访问性集中在UISegmentedControl中的特定段上-LMLPHP

解决方案是使用selectedIndex 来显示选定的段,然后VoiceOver notification提供适当的段对象:很简单,不是吗?

我天真地以为,用selectedIndex在分段控件子视图数组中获得子视图会完成这项工作,但这绝对不可能,因为可以使子视图在该数组中移动,因为以下快照突出显示了(例如,红色边框的第一个元素):
ios - 如何将可访问性集中在UISegmentedControl中的特定段上-LMLPHP
标识唯一段的唯一方法是它的框架,因此我选择了分段的控制索引和所选段的框架,以将它们传递给先前的视图控制器。

当转换后此屏幕出现时,将允许显示(索引)和读出(标识用于通知的对象的框架)适当的所选段。

此后,包含“下一个屏幕”按钮的视图控制器的代码片段:

class SOFSegmentedControl: UIViewController, UpdateSegmentedIndexDelegate {

    var segmentIndex = 0
    var segmentFrame = CGRect.zero


    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        if let segueName = segue.identifier {
            if (segueName == "SegmentSegue") {
                if let destVC = segue.destination as? SOFSegmentedControlBis {

                    destVC.delegate = self
                    destVC.segmentIndex = segmentIndex
                    destVC.segmentFrame = segmentFrame
                }
            }
        }
    }


    @IBAction func buttonAction(_ sender: UIButton) { self.performSegue(withIdentifier: "SegmentSegue", sender: sender) }


    func updateSegmentIndex(_ index: Int, withFrame frame: CGRect) {

        segmentIndex = index
        segmentFrame = frame
    }
}

...,对于显示分段控件的视图控制器:
protocol UpdateSegmentedIndexDelegate: class {
    func updateSegmentIndex(_ index: Int, withFrame frame: CGRect)
}


class SOFSegmentedControlBis: UIViewController {

    @IBOutlet weak var mySegmentedControl: UISegmentedControl!

    var delegate: UpdateSegmentedIndexDelegate?
    var segmentFrame = CGRect.zero
    var segmentIndex = 0
    var segmentFrames = [Int:CGRect]()


    override func viewDidLoad() {
        super.viewDidLoad()

        mySegmentedControl.addTarget(self,
                                     action: #selector(segmentedControlValueChanged(_:)),
                                     for: .valueChanged)

        mySegmentedControl.selectedSegmentIndex = segmentIndex
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        print(mySegmentedControl.subviews)

        let sortedFrames = mySegmentedControl.subviews.sorted(by: { $0.frame.origin.x < $1.frame.origin.x})

        for (index, segment) in sortedFrames.enumerated() { segmentFrames[index] = segment.frame }

        if (self.segmentFrame == CGRect.zero) {
            UIAccessibility.post(notification: .screenChanged,
                                 argument: mySegmentedControl)
        } else {
            mySegmentedControl.subviews.forEach({
                if ($0.frame == self.segmentFrame) {
                    UIAccessibility.post(notification: .screenChanged,
                                         argument: $0)
                }
            })
        }
    }


    @objc func segmentedControlValueChanged(_ notif: NSNotification) {

        delegate?.updateSegmentIndex(mySegmentedControl.selectedSegmentIndex,
                                     withFrame: segmentFrames[mySegmentedControl.selectedSegmentIndex]!) }
    }

最终结果如下:
ios - 如何将可访问性集中在UISegmentedControl中的特定段上-LMLPHP
ios - 如何将可访问性集中在UISegmentedControl中的特定段上-LMLPHP
  • 双击进入下一个屏幕。
  • 选择下一个元素以聚焦第二个片段。
  • 双击以选择聚焦的元素。
  • 多亏了iOS本身带有导航控制器的 Z gesture 才能返回上一屏幕。委托传递索引和所选段的框架。
  • 双击进入下一个屏幕。
  • VoiceOver会读出以前选择的段,并且仍会选择该段。

  • 现在,您可以按照以下基本原理在UISegmentedControl 中聚焦于特定段的可访问性。

    我尝试避免使用“hacky”解决方案(就像我刚才提到的那样),但是我愿意考虑任何事情。

    不幸的是,该解决方案是一个棘手的问题。但是,它可以正常工作,而且我在其他任何地方都找不到:将其视为个人修复,除非您可以与他人共享干净的工具? ; o)

    更新...这可能是第二个问题。

    我无法重现您的更新行为:如果您为此问题创建专门的主题,请添加最详细的代码和上下文,以便提供最准确的解决方案。

    10-08 02:46