GarySabo GarySabo - 2 months ago 20
iOS Question

Setting preferred focus in TVOS on an SKSpriteNode?

Since I'm working in SpriteKit, my buttons are

SKSpriteNodes
...however I find myself in a situation where I need to set focus in my
viewController
via overriding
preferredFocusedView
. Is there a way to downcast an
SKSpriteNode
to a
UIView
? If so I haven't been able to figure out yet...any alternative?

let playButton = SKSpriteNode(imageNamed: "PlayButton")
playButton.position = CGPoint(x: scene.size.width * 0.25, y: scene.size.height * 0.25)
playButton.zPosition = Layer.UI.rawValue
scene.worldNode.addChild(playButton)

override var preferredFocusedView: UIView? {
get {
return //playButton how?
}
}

Answer

Focus navigation is only now supported with tvOS 10 and SpriteKit, prior to that you had to do it manually using your own focus system.

For that reason preferred focus view is depreciated because it only supports UIViews. You should now use preferred focus environments instead.

First thing you do is in your GameViewController set the preferred focus environment to the first presented scene.

class GameViewController: UIViewController {

     static var currentScene: SKScene? // static because I have multiple scenes and need to change this value easily


      override func viewDidLoad() {
           super.viewDidLoad()

           let scene = StartScene(...)
           GameViewController.currentScene = scene
           ...
      }

      override var preferredFocusEnvironments: [UIFocusEnvironment] {
           if let scene = GameViewController.currentScene {
               return [scene]
           } 

           return []
      }
 }

Than in your 1st SKScene you can set the focus environment to your playButton.

 override var preferredFocusEnvironments: [UIFocusEnvironment] {
         return [playButton]
   }

You play button should be a subclass of SKSpriteNode that you use for all your buttons in your game. Use enums and give them different names or identifiers to distinguish between them.

 class Button: SKSpriteNode {

      var isFocusable = true // easy way to later turn off focus for your buttons e.g. when overlaying menus etc.

      /// Can become focused
      override open var canBecomeFocused: Bool {
          return isFocusable
      }

      /// Did update focus
      override open func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {

          if context.previouslyFocusedItem === self {
              // SKAction to reset focus animation for unfocused button
          }

          if context.nextFocusedItem === self {
               // SKAction to run focus animation for focused button
          }
      }
 }

For more detail you should read this question and answer I posted recently.

TVOS 10 SpriteKit Focus Navigation default focused item

You should also read this article

https://medium.com/folded-plane/tvos-10-getting-started-with-spritekit-and-focus-engine-53d8ef3b34f3#.x5zty39pc

and watch the 2016 apple keynote called "Whats New in SpriteKit" where they talk about it half way through.

Hope this helps