你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> iOS VoiceOver編程指南

iOS VoiceOver編程指南

編輯:IOS開發基礎

1460947889775601.jpg

前言

VoiceOver是蘋果“讀屏”技術的名稱,屬於輔助功能的一部分。VoiceOver可以讀出屏幕上的信息,以幫助盲人進行人機交互。 這項技術在蘋果的各個系統中都可以看到,OS X,iOS,watchOS,甚至tvOS。 蘋果公司的VoiceOver在2015年6月18日獲得了美國盲人基金會(American Foundation for the Blind, AFB)頒發的海倫凱勒成就獎,成為全球首家獲得此殊榮的科技公司。 單從iOS來說,iOS的VoiceOver功能可以毫不誇張的說是三大移動平台中做的最好的。

雖然說蘋果默認的UI組件都已經默認支持VoiceOver功能了,但是通常情況下App還是需要對VoiceOver進行適配和優化的,比如說一些自定義復雜UI組件。

基本使用

iPhone上開啟VoiceOver功能後,就可以通過 單指左右輕掃 來遍歷當前界面中的所有的AccessibilityElement(可以被VoiceOver訪問的UI元素),當一個AccessibilityElement被選中後,VoiceOver會將AccessibilityElement的信息讀出來。 單指輕點兩次 能夠激活當前元素對應的操作,比如當前AccessibilityElement是一個按鈕,那麼對應的就是按鈕的Action事件。

簡單點來說在App開發過程中關於VoiceOver我們需要關注如下幾點:

  • 界面上的AccessibilityElement有哪些

  • AccessibilityElement的位置和形狀

  • AccessibilityElement的信息是什麼(就是Element被選中後,被讀出來內容)

  • AccessibilityElement所能響應的的事件有哪些

UIKit中的控件基本都是 VoiceOver Ready的,即使是UIView,你也可以通過簡單的設置其實變成AccessibilityElement。所以這一小節中所講的AccessibilityElement其實都是UIView或其子類的實例。 

相關屬性和方法基本都在UIAccessibility.h這個頭文件中進行了聲明。

所以簡單的情況下通過UIView的isAccessibilityElement屬性就可以控制某個View是否是AccessibilityElement,在UIKit的控件中,像UILabel,UIButton 這些控件的isAccessibilityElement屬性默認就是true的,UIView這個屬性默認是false。

一般情況下AccessibilityElement的位置和形狀是通過accessibilityFrame進行設置的,默認值是View在屏幕中的位置,形狀就是View的矩形形狀。如果你想自己設置accessibilityFrame的值,那麼得注意下,這邊的frame值是相對於設備Screen的坐標系的,當然可以通過UIAccessibilityConvertFrameToScreenCoordinates函數來幫助轉換。此函數有兩個參數,一個rect,一個是view, 其含義就是將相對於view這個坐標系的rect轉換成相對於screen坐標系的值並返回。所以一般情況下 rect可以是目標Element在父View中的frame,view就為其父view。

public func UIAccessibilityConvertFrameToScreenCoordinates(rect: CGRect, _ view: UIView) -> CGRect

如果你想設置非矩形的形狀,你也可以通過給 accessibilityPath 屬性指定一個UIBezierPath類型的值來自定義AccessibilityElement的形狀。

至於AccessibilityElement的信息可以通過下面幾個UIAccessibility的屬性來決定

  • accessibilityLabel 這是什麼

  • accessibilityHint 這個有什麼用,會產生什麼樣的結果

  • accessibilityValue 這個的 值 是什麼

  • accessibilityTraits 這個的類型以及狀態,就是通過traits來表征這個Element的特質,數據類型是一個枚舉類型,可以通過按位或的方式合並多個特性。

這裡有個需要注意的就是,當某個View的是AccessibilityElement的時候 ,其subviews都會被屏蔽掉,這個特性有時候還是有用的,比如一個View中包含多個Label,那麼你希望每一個下面的Label不要單獨可以訪問到,那麼你可以將這個View設置成可以訪問的,然後將其accessibilityLabel設置為所有子Label的accessibilityLabel的合並值。

至於AccessibilityElement的事件,最簡單的莫過於上面提到 單指輕點兩次 能夠激活當前元素對應的操作了,如果當前AccessibilityElement實現的public func accessibilityActivate() -> Bool這個方法返回true,那邊此邏輯將被調用,否則相當於在AccessibilityElement的accessibilityActivationPoint這個位置點上進行了一次Tap操作。

高級特性

Accessibility Container 設想下這樣的一個場景,一個UIView,內部包含一組用戶可以進行交互的內容,每一個內容之間是獨立的,但是這些內容不是以子View的形式存在,而是通過Quarz 2D或者Core Text渲染而成,所以這部分內容無法通過上面的方式變成AccessibilityElement。這種情況UIView需要按照UIAccessibilityContainer的方式,來將內部的每一個獨立的內容都描述成UIAccessibilityElement的實例,這個時候這個UIView我們稱之為Accessibility Container。

接下來講解具體的實現步驟了。 先說說iOS 8之後如何實現,首先Accessibility Container的isAccessibilityElement值必須設置為false,另外我們需要創建出所有的UIAccessibilityElement的實例,然後賦給accessibilityElements屬性(iOS 8.0+) 假設在一個UIView的子類中,通過叫做updateAccessibleElements的方法來更新維護所有的UIAccessibilityElement實例,當界面上的內容發生變化,或者VoiceOver開啟關閉狀態發生變化時,調用此方法以更新accessibilityElements,相關偽代碼如下:

internal func updateAccessibleElements() {
   guard UIAccessibilityIsVoiceOverRunning() else {
        self.accessibilityElements = nil
        return
   }
        
   self.isAccessibilityElement = false
   var elements = [AnyObject]()
   let element1 = UIAccessibilityElement(accessibilityContainer: self)
   element1.accessibilityLabel = "element1"
   element1.accessibilityTraits = UIAccessibilityTraitStaticText
   element1.accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(element1FrameInSelf, self)
   elements.append(element1)
        ...
   self.accessibilityElements = elements
}

如果是iOS 8以下,那麼就需要實現下面的幾個方法來實現了,比較繁瑣:

// accessibilityElement的個數
public func accessibilityElementCount() -> Int
// 返回指定Index的accessibilityElement
public func accessibilityElementAtIndex(index: Int) -> AnyObject?
// 返回指定accessibilityElement的Index
public func indexOfAccessibilityElement(element: AnyObject) -> Int

使用上面第二種方式的時候,往往需要自己維護一個包含所有accessibilityElements的數組,然後通過數組來完成上面的這三個方法的返回。這種方法比較累贅,而且當界面更新需要刷新內容的時候,還需要發送通知系統,告訴系統界面上的accessibilityElements有變動需要更新。所以最小版本定位於iOS 8的情況下還是直接設置accessibilityElements屬性的方式比較科學。

關於UIAccessibilityElement這個類的設計還是存在一些疑惑的,既然NSObject的UIAccessibility擴展已經包含了諸如accessibilityLabel,accessibilityHint這些屬性,為何UIAccessibilityElement類中還需要重復聲明這些屬性,有點重復的感覺。

Actions

之前只講到了最簡單的事件,就是單指輕點兩下,其實常見的Actions有下面這些,每一個Action都會對應一個方法,可以通過覆蓋方法的方式來自定義Action對應的邏輯:

  • Activate 單指輕點兩次 

public func accessibilityActivate() -> Bool
  • Escape. 單指 Z-shaped 手勢一般用於退出模態界面或者返回導航的上一頁界面 

public func accessibilityPerformEscape() -> Bool
  • Magic Tap. 雙指輕點兩次觸發 most-intended action. 

public func accessibilityPerformMagicTap() -> Bool
  • Three-Finger Scroll. 三指滑動觸發界面水平或者垂直的滾動 

public func accessibilityScroll(direction: UIAccessibilityScrollDirection) -> Bool
  • Increment. 單指向上滑動,需要設置accessibilityTraits為UIAccessibilityTraitAdjustable,否則對應的方法不會被調用 

public func accessibilityIncrement()
  • Decrement. 單指向下滑動,需要設置accessibilityTraits為UIAccessibilityTraitAdjustable,否則對應的方法不會被調用 

public func accessibilityDecrement()

這些方法中,其中Escape,Magic Tap,Three-Finger Scroll這幾種手勢支持在響應鏈中向上尋找對應的Action方法,首先用戶在屏幕上進行相應的手勢,系統檢查當前VoiceOver的Focus的Element有無實現對應的方法,沒有實現的話,則向響應鏈的上一級尋找。比如有些全局性的操作,對應的方法寫在上層View或者ViewController中比較合適。 其實很多系統提供的組件都默認實現了一些VoiceOver的手勢Action,比如UINavigationController, UIAlertController 都提供了對Escape手勢的支持。

Accessibility Notification

Accessibility提供了一系列的通知,可以完成一些特定的需求。比如你可以監聽UIAccessibilityVoiceOverStatusChanged通知,來監控Voice Over功能開啟關閉的實時通知 。

或者是你在App中主動發送一些通知,來讓系統做出一些變化,比如當你界面上的AccessibilityElement有變動的時候你可以發送UIAccessibilityLayoutChangedNotification通知,通知發送時使用UIAccessibility的專用的函數,UIAccessibilityPostNotification函數有兩個參數,第一個是通知名,第二個是你想讓VoiceOver讀出來的字符串或者是新的VoiceOver的焦點對應的元素。

UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, self.myFirstElement)

建議

AccessibilityElement的信息盡量簡潔,accessibilityLabel不要包含提示性質的文案,避免信息干擾

TableView的每一個Cell的信息盡量合並,使得Cell變成一個整體的AccessibilityElement,避免無意義的冗余元素之間切換的操作。Cell中有多個按鈕的時候,可以考慮使用Magic Tap的方式,Magic Tap的Action中彈出sheet樣式的UIAlertController來供用戶操作。

自定義的模態頁面注意設置accessibilityViewIsModal為true,最好支持Escape手勢 的方式退出模態頁面。

將頁面中裝飾用的沒有實際意義的元素的accessibilityElementsHidden設置成true,減少操作過程中的干擾

參考文檔: UIAccessibility Protocol Supporting Accessibility Accessibility Programming Guide for iOS

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved