在進行無障礙適配的過程中,有一個非常煩人的問題,就是 VoiceOver 朗讀控制元件的順序。通常來講,VoiceOver 會用從上到下從左到右的順序朗讀控制元件,但是也有一些時候會讀的亂七八糟。而且還存在一些我們需要修改這一順序來讓盲人用起來更方便的情況。
StackOverFlow 中有這樣一個回答,其中描述了通過 Tag 來重建 isAccessibilityElement 順序的方法,但是這一方法並不十分靠譜。首先設定 Tag 也並不方便,而且很多時候 Tag 會被用來幹其他的事情,只使用 Tag 會帶來很多問題。而且這個方法似乎壓根不能用,我在測試的時候發現 isAccessibilityElement 一直是空的,不知道是不是我讀取時機的問題。
於是我昨天晚上研究了一陣子,找到了一個很方便的方案,也已經寫在了這個問題的回答中。和 @TejAces 的回答類似,我也採用了讀 subview 陣列的方案,新建一個 Swift 檔案,輸入以下內容:
import UIKit
extension UIView {
func updateOrder(_ direction: Bool = true) {
var tempElements: [Any]? = [Any]()
let views = (direction) ? subviews : subviews.reversed()
for aView in views {
tempElements?.append(aView)
}
accessibilityElements = tempElements
}
}
class ReorderAccessibilityByStoryBoardView: UIView {
override func didAddSubview(_ subview: UIView) {
updateOrder()
}
}
複製程式碼
在 Storyboard 或者程式碼中,將你需要修改其中控制元件順序的 View 的 Class 設定為 ReorderAccessibilityByStoryBoardView,接下來你只需要調整 StoryBoard 控制元件列表中的排列順序,即可直接反應在 VoiceOver 中,非常方便快捷。對於從程式碼新增的 View,你只需要按照想要的順序來 addSubview() 就行了。
由於 StackView 和 ScrollView 等比較特殊,如果直接在他們的父 View 上設定 Class,並不能夠讀到其中的內容,所以需要在檔案裡單獨寫一個 Class 來處理:
class ReorderAccessibilityByStoryBoardStackView: UIStackView {
override func didAddSubview(_ subview: UIView) {
updateOrder(false)
}
}
複製程式碼
之後同樣按照之前的方法設定好就可以了。
你還可以在 StackOverFlow 中檢視我的回答。
2018-9-8:我發現在 didAddSubview 這個生命週期的時候,控制元件的 Accessibility 資訊其實還沒有被載入,判斷是否隱藏是沒有意義的。在沒有找到更好的方法之前,我暫時去掉這些程式碼。
在使用時候請儘可能選擇子 View 來設定,而不是直接設定整個畫面為 ReorderAccessibilityByStoryBoardView,一來節省資源,也可以防止設定為 Accessibility Hidden 的控制元件被朗讀。
此外在使用之前,應該先嚐試是否能通過設定 UIView 提供的 shouldGroupAccessibilityChildren 屬性來將控制元件分組,不能的話再使用此方法。