在兩年前,我曾寫過一篇名為《ios 為視障用戶支持 VoiceOver》的文章,裡邊主要介紹了 iOS 端該如何為 VoiceOver 進行必要的支持,後來我又開發了 macOS 端的落格輸入法,但很遺憾由於 macOS 自身系統 bug,第三方輸入法根本無法獲得 VoiceOver 焦點(主要是 10.13 及以下版本),所以我也就沒有過多關注——甚至直到這款輸入法整個生命週期結束也沒能實現 VoiceOver 的支持,實在是讓人遺憾。
落格輸入法 macOS 2
不過還好,到 macOS 10.14 莫哈韋,神奇的事情出現了,這個 bug 完全“反轉”了,它並沒有修復,但以另一種方式呈現——系統自帶的中文輸入法無法獲取 VoiceOver 焦點,反而第三方輸入法可以了! 😆
總之,這次我也得以能夠為 VoiceOver 用戶提供支持 :)
直到現在,落格輸入法 macOS 2 也已經正式上架銷售了一段時間,產品也相對第一個版本完善了許多,我覺得是時候寫一篇文章來粗略總結一下 macOS 平台對 VoiceOver 支持的重點。
決策
要支持 VoiceOver,你就必須能夠判斷系統當前是否正在運行 VoiceOver,不同於 iOS,macOS 並不直接給出 VoiceOver 是否在運行的判斷 API,也就是說,從官方的態度來看,還是希望視障用戶能和明眼用戶得到盡可能一致的用戶體驗的。但總之,針對視障用戶做特定的額外優化還是必要的,我們可以通過讀取系統的偏好設置來判斷 VoiceOver 是否打開——但這也有缺點,比如用戶打開了 VoiceOver,但不經過系統偏好設置直接關閉了 VoiceOver,那此時我們依舊得到的是“開啟”的狀態(對 VoiceOver 狀態特別敏感的開發者需要注意):
1 2 3 4 5 6 7 8 9 10 |
func NSIsVoiceOverRunning() -> Bool { if let flag = CFPreferencesCopyAppValue("voiceOverOnOffKey" as CFString, "com.apple.universalaccess" as CFString) { if let voiceOverOn = flag as? Bool { return voiceOverOn } } return false } |
設置工具方面
如果你都使用了系統提供的控件,那麼設置項和 iOS 沒什麼區別,值得一提的就是系統自帶的圓圈問號按鈕,也就是在一些 app 裡常見的 help 按鈕,它默認的名稱叫“help”,而 storyboard 中修改的地方叫做“描述“,這讓人多少覺得有些混淆,作為對比,iOS 則對應的輸入框,名稱叫 “標題“。
候選欄
對於輸入法來說,候選欄可能是最常用的一個界面了,這也是它主要的 UI 部分。為了提供更完善的自定義和更優秀的顯示性能,實際上我並沒有使用 macOS
InputMethodKit 中自帶的那個候選欄,那個候選欄除了能簡單“顯示候選”這一個功能外,其他所有自定義選項都是無效的,更別提這些選項之外的自定義功能了,更是不可能。為此我自己實現了一個候選欄,但是在 VoiceOver 下,讀取的時候是這樣的順序:
1→第一個候選內容→候選的背景→2→第二個候選的內容→3→……
這並不是一個很好的體驗,尤其是當選中的內容是123這些數字的時候,用戶甚至不知道上屏的是什麼——實際上你的輸入法也不知道要選中什麼。
NSAccessibilityGroup 協議
這個問題主要是 VoiceOver 把你 View 都給遍歷了,它無法區分哪些有用哪些沒用,這時候我們用一個 View 包裹這些元素(實際上你本來也應該這麼做,畢竟這是一個“候選”對象,對吧? ),然後讓你的 View 遵循 NSAccessibilityGroup 這個協議,這時對 VoiceOver 來說,它就是一個唯一的元素了,VoiceOver 不會繼續遍歷改 View 下的 SubView。
不過,這時候新的問題產生了,這些 Group 沒了內容,因為是自定義的 Group,我們需要手動來為這個 Group 賦值。
要給 Group 賦值,不同於 iOS,macOS 下你還是需要用方法來實現而不是直接給屬性賦值,比如 自.setAccessibilityTitle("候選1") 但事實上這樣賦值也不會生效,我不知道這是對於輸入法的特殊需求,還是說其實是個系統的 bug,總之,我嘗試重寫這些方法,最終成功了:
1 2 3 4 |
override func accessibilityLabel() -> String? { return "候选1" } |
這樣,當用戶移動 VoiceOver 焦點到對應的候選上時,VoiceOver 就會讀出候選的內容了(這裡就是“候選1”)。當然,這還不夠,你會發現視覺上 VoiceOver 的方框是跑偏的,所以你還需要設置它的位置,儘管也許視障用戶並不介意這些小細節:
1 2 3 4 5 6 |
override func accessibilityFrame() -> NSRect { if let r = self.superview?.window?.convertToScreen(self.frame) { return r } return self.frame } |
這裡我們嘗試獲取當前候選欄的 window,並將候選的位置信息轉換到全局屏幕坐標並返回(VoiceOver 要的坐標必須是屏幕級別),這樣,VoiceOver 的焦點方框也就對正了。
最後是 Group 的身份問題,如果我們不設置,那麼用戶即使把 VoiceOver 焦點移動到了對應的候選上,按下空格後可能上屏的依舊是第一個,因為 VoiceOver 有一套它自己的控制快捷鍵,所以,這裡也要支持一下:
1 2 3 4 5 6 7 |
override func accessibilityRole() -> NSAccessibility.Role? { return .button//设置为 button 角色,这样才能允许交互 } override func accessibilityPerformPress() -> Bool { return true//返回 true 表示接受了本次操作 } |
這樣候選欄部分基本就完成了。
發送 NSAccessibility 通知
如同 iOS 一樣,有時候我們需要給 VoiceOver 發送通知以觸髮刷新,macOS 也支持你這麼做:
1 2 3 4 5 6 7 8 |
extension NSAccessibility.Notification { …… public static let applicationHidden: NSAccessibility.Notification public static let focusedUIElementChanged: NSAccessibility.Notification …… } |
與 iOS 不同的是,桌面級別的通知多了很多,這裡我只用到了上面列舉的兩個,前者用來通知候選欄已經隱藏,後者用來觸發 VoiceOver 刷新。
用戶體驗
狀態提示
不同於系統自帶中文輸入法,落格輸入法和其他第三方輸入法一樣,是支持中英文模式切換的,這其實對視障用戶帶來了不小的困惑,因為他們不知道當前的狀態是什麼,並且視覺上的小方塊提示也無法通知到他們,在與視障用戶交流之後,我做了一個簡單的狀態提示音,在切換到中文或者英文時,發出不同的提示音用以通知。
另外,由於中英切換的那個提示小方塊實際上也是一個 window,那就存在一個問題,如果你切換中英文模式或者繁簡功能,那麼這個小方塊的出現會搶奪 VoiceOver 焦點,這時候就需要讓它對 VoiceOver 不可見:
1 2 3 |
w.setAccessibilityHidden(true) w.setAccessibilityElement(false) w.setAccessibilityEnabled(false) |
無限候選欄
平時,候選欄是翻頁設計的,為了方便視障用戶(其實有時候明眼用戶也用得到)選字,落格輸入法 macOS 2 支持自動翻頁,比如當前有 16 個候選,候選欄默認顯示了 6 個(第一頁),那麼你移動高亮到第六個的時候繼續移動,會自動翻頁並定位到第二頁的第一個。
解字不讀詞
如同明眼人一樣,視障用戶也會追求輸入的效率,VoiceOver 在讀候選詞的時候會先把整個候選完整讀一遍,然後再逐字解釋,這對整句輸入用戶來說可能就很方便,一次輸入好幾個字,然後聽一遍發現沒大問題,就發送了,根本不用等後邊的逐字解釋,方便的很——但這樣有個弊端就是中文字符同音太多了,經常會導致一些錯字在裡面,就是明眼人都在所難免,何況是靠聽(比如:幫我佔個座尾巴)。對於喜歡打單或是打詞的用戶,其實只解釋字要更高效,在 空山新雨 的建議下我添加了落格輸入法 macOS 2 特有的“只解字不讀詞”模式,不論是落格解釋庫還是經典解釋庫,都可以直接支持,方便的很。
已知問題
當然,最終還是少不了一些遺留問題:
候選欄的名稱改不了,興許是因為輸入法是個後台應用導致的?不論為 window 設置什麼名字,VoiceOver 下永遠讀的是“application”。
另一個是輸入完會觸發全文朗讀的問題,雖然聊天發言估計問題嚴重性不大,但如果你是在寫一篇長篇的文章,那就有點讓人崩潰了……但遺憾的是,我沒有任何辦法能夠打斷這個過程,目前唯一的解是用戶輸入完自己手動移動一下光標……
還有一個記不太清的問題,在 macOS 上,主動發通知讓 VoiceOver 朗讀內容似乎是無法生效的。
本文由 落格博客 原創撰寫:落格博客 » 落格輸入法 macOS 2 是如何為 VoiceOver 進行優化的
轉載請保留出處和原文鏈接:https://www.logcg.com/archives/3153.html