Sprite Kit 的辅助功能(画外音)

Posted

技术标签:

【中文标题】Sprite Kit 的辅助功能(画外音)【英文标题】:Accessibility (Voice Over) with Sprite Kit 【发布时间】:2015-05-12 13:09:48 【问题描述】:

我正在尝试在具有固定棋盘的益智游戏中添加对旁白辅助功能的支持。但是,我无法让 UIAccessibilityElements 出现。

现在我在我的 SKScene 中覆盖 accessibilityElementAtIndexaccessibilityElementCountindexOfAccessibilityElement

他们正在返回一个可访问元素的数组,如下所示:

func loadAccessibleElements()

    self.isAccessibilityElement = false

    let pieces = getAllPieces()

    accessibleElements.removeAll(keepCapacity: false)
    for piece in pieces
    
        let element = UIAccessibilityElement(accessibilityContainer: self.usableView!)

        element.accessibilityFrame = piece.getAccessibilityFrame()
        element.accessibilityLabel = piece.getText()
        element.accessibilityTraits = UIAccessibilityTraitButton
        accessibleElements.append(element)
    

其中piece是SKSpriteNode的子类,getAccessibilityFrame被定义:

func getAccessibilityFrame() -> CGRect

    return parentView!.convertRect(frame, toView: nil)

现在一个(大小错误)accessibility 元素似乎出现在屏幕上的错误位置。

有人能指出我正确的方向吗?

非常感谢

编辑: 我已经尝试了一个 hack-ish 解决方法,方法是在 SKView 上放置一个 UIView,其中 UIButton 元素与 SKSpriteNodes 位于同一位置。但是,可访问性仍然不想工作。视图是这样加载的:

func loadAccessibilityView()

    view.isAccessibilityElement = false
    view.accessibilityElementsHidden = false
    skView.accessibilityElementsHidden = false
    let accessibleSubview = UIView(frame: view.frame)
    accessibleSubview.userInteractionEnabled = true
    accessibleSubview.isAccessibilityElement = false
    view.addSubview(accessibleSubview)
    view.bringSubviewToFront(accessibleSubview)

    let pieces = (skView.scene! as! GameScene).getAllPieces()
    for piece in pieces
    
        let pieceButton = UIButton(frame: piece.getAccessibilityFrame())
        pieceButton.isAccessibilityElement = true
        pieceButton.accessibilityElementsHidden = false
        pieceButton.accessibilityTraits = UIAccessibilityTraitButton
        pieceButton.setTitle(piece.getText(), forState: UIControlState.Normal)
        pieceButton.setBackgroundImage(UIImage(named: "blue-button"), forState: UIControlState.Normal)
        pieceButton.alpha = 0.2
        pieceButton.accessibilityLabel = piece.getText()
        pieceButton.accessibilityFrame = pieceButton.frame
        pieceButton.addTarget(self, action: Selector("didTap:"), forControlEvents: UIControlEvents.TouchUpInside)
        accessibleSubview.addSubview(pieceButton)
    

    UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil)


按钮放置正确,但可访问性根本不起作用。似乎有什么东西阻止了它的工作。

【问题讨论】:

除此之外,我尝试了另一种方法 - 我添加了 UIButtons 的子视图,以覆盖与 skspritenodes 完全相同的位置。我很惊讶这些按钮也无法通过语音访问。我是否犯了错误阻止输入? skscene 本身确实会覆盖 touchesBegun(...) 等。 【参考方案1】:

我徒劳地搜索了有关如何使用 SpriteKit 在 Swift 中实现 VoiceOver 的描述,所以我终于想出了如何去做。下面是一些在添加到 SKScene 类时将 SKNode 转换为可访问按钮的工作代码:

// Add the following code to a scene where you want to make the SKNode variable named “leave” an accessible button
// leave must already be initialized and added as a child of the scene, or a child of other SKNodes in the scene
// screenHeight must already be defined as the height of the device screen, in points

// Accessibility

private var accessibleElements: [UIAccessibilityElement] = []

private func nodeToDevicePointsFrame(node: SKNode) -> CGRect 

    // first convert from frame in SKNode to frame in SKScene's coordinates

    var sceneFrame = node.frame
    sceneFrame.origin = node.scene!.convertPoint(node.frame.origin, fromNode: node.parent!)

    // convert frame from SKScene coordinates to device points
    // sprite kit scene origin is in lower left, accessibility device screen origin is at upper left
    // assumes scene is initialized using SKSceneScaleMode.Fill using dimensions same as device points

    var deviceFrame = sceneFrame
    deviceFrame.origin.y = CGFloat(screenHeight-1) - (sceneFrame.origin.y + sceneFrame.size.height)
    return deviceFrame


private func initAccessibility() 
    if accessibleElements.count == 0 
        let accessibleLeave = UIAccessibilityElement(accessibilityContainer: self.view!)
        accessibleLeave.accessibilityFrame = nodeToDevicePointsFrame(leave)
        accessibleLeave.accessibilityTraits = UIAccessibilityTraitButton
        accessibleLeave.accessibilityLabel = “leave” // the accessible name of the button
        accessibleElements.append(accessibleLeave)
    

override func didMoveToView(view: SKView) 
    self.isAccessibilityElement = false
    leave.isAccessibilityElement = true


override func willMoveFromView(view: SKView) 
    accessibleElements = []


override func accessibilityElementCount() -> Int 
    initAccessibility()
    return accessibleElements.count


override func accessibilityElementAtIndex(index: Int) -> AnyObject? 
    initAccessibility()
    if (index < accessibleElements.count) 
        return accessibleElements[index] as AnyObject
     else 
        return nil
    


override func indexOfAccessibilityElement(element: AnyObject) -> Int 
    initAccessibility()
    return accessibleElements.indexOf(element as! UIAccessibilityElement)!

【讨论】:

就我而言,覆盖节点子类上的accessibilityFrame 并设置它的标签和特征就足够了。【参考方案2】:

辅助功能框架是在固定的物理屏幕坐标中定义的,而不是 UIView 坐标,并且它们之间的转换有点棘手。

当设备处于横向模式时,设备原点是屏幕的左下角,X 向上。

这是一个痛苦的转换,我不知道为什么苹果会这样做。

【讨论】:

是的,它们很麻烦,我已经尝试了 getAccessibilityFrame 函数的一些变体——所有这些都不能正常工作。我已经设法将 UIButtons 覆盖在精灵元素上,但可访问性也不适用于它们。我觉得也许 SKScene 混淆了输入?点击并拖动它只会发出噪音,就像您试图选择一个无法访问的空白区域一样。我很快会在我的问题中扩展它 我很幸运,应用规范只需要左右横向,因此当它们不能始终如一地工作时,很容易手动编写转换代码。 可访问性元素只是点击或手势的焦点区域,因此任何阻止它的东西都会使其无法正常工作。您确定您的可访问性容器(如果有的话)返回“否”吗? 在 Swift 中,我似乎无法直接覆盖它以返回 false,但我将与容器对象关联的 UIView 的变量 isAccessibilityElement 设置为 false。使用我的新(尽管是 hackish)UIButton 方法,我假设可访问性会像其他视图控制器中的按钮一样自动处理,但似乎有些不对劲。我将编辑我的主要帖子以包含一些详细信息 我从未在 ios6 或 iOS7 上解决此问题,但 VO 焦点和切换焦点在 iOS 8 及更高版本中按预期工作,即跟随窗口而不是设备方向。

以上是关于Sprite Kit 的辅助功能(画外音)的主要内容,如果未能解决你的问题,请参考以下文章

在哪里可以找到 iOS“配音”功能的 emoji 辅助功能文本?

什么被调用而不是 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 当辅助功能(画外音打开)

更改焦点时不读取辅助功能提示

覆盖 TextField 中的 VoiceOver 消息 - iOS 辅助功能

有没有办法在导航栏后退按钮上自定义辅助功能标签?

IOS辅助功能选择的标记不移动