使用Sprite Kit的辅助功能(配音)

时间:2023-01-12 16:54:03

I'm attempting to add support for Voice Over accessibility in a puzzle game which has a fixed board. However, I'm having trouble getting UIAccessibilityElements to show up.

我试图在具有固定板的益智游戏中添加对Voice Over accessibility的支持。但是,我无法让UIAccessibilityElements显示出来。

Right now I'm overriding accessibilityElementAtIndex, accessibilityElementCount and indexOfAccessibilityElement in my SKScene.

现在我在我的SKScene中覆盖了accessibilityElementAtIndex,accessibilityElementCount和indexOfAccessibilityElement。

They are returning an array of accessible elements as such:

他们返回一组可访问的元素:

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)
    }
}

Where piece is a subclass of SKSpriteNode and getAccessibilityFrame is defined:

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

func getAccessibilityFrame() -> CGRect
{
    return parentView!.convertRect(frame, toView: nil)
}

Right now one (wrongly sized) accessibility element seems to appear on the screen in the wrong place.

现在,一个(尺寸错误的)辅助功能元素似乎出现在错误的地方。

Could someone point me in the right direction?

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

Many thanks

EDIT:
I've tried a hack-ish work around by placing a UIView over the SKView with UIButton elements in the same location as the SKSpriteNodes. However, accessibility still doesn't want to work. The view is loaded as such:

编辑:我已经尝试了一个hack-ish工作,将UIView放在SKView上,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)

}

The buttons are placed correctly, however accessibility just isn't working at all. Something seems to be preventing it from working.

按钮放置正确,但可访问性完全不起作用。似乎有些东西阻止它起作用。

2 个解决方案

#1


2  

I've searched in vain for a description of how to implement VoiceOver in Swift using SpriteKit, so I finally figured out how to do it. Here's some working code that converts a SKNode to an accessible pushbutton when added to a SKScene class:

我搜索到如何使用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)!
}

#2


0  

Accessibility frames are defined in the fixed physical screen coordinates, not UIView coordinates, and transforming between them is kind of tricky.

可访问性框架在固定的物理屏幕坐标中定义,而不是UIView坐标,并且在它们之间进行转换是一种棘手的方法。

The device origin is the lower left of the screen, with X up, when the device is in landscape right mode.

当设备处于横向右侧模式时,设备原点位于屏幕的左下方,X向上。

It's a pain converting, I've no idea why Apple did it that way.

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

#1


2  

I've searched in vain for a description of how to implement VoiceOver in Swift using SpriteKit, so I finally figured out how to do it. Here's some working code that converts a SKNode to an accessible pushbutton when added to a SKScene class:

我搜索到如何使用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)!
}

#2


0  

Accessibility frames are defined in the fixed physical screen coordinates, not UIView coordinates, and transforming between them is kind of tricky.

可访问性框架在固定的物理屏幕坐标中定义,而不是UIView坐标,并且在它们之间进行转换是一种棘手的方法。

The device origin is the lower left of the screen, with X up, when the device is in landscape right mode.

当设备处于横向右侧模式时,设备原点位于屏幕的左下方,X向上。

It's a pain converting, I've no idea why Apple did it that way.

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