5

I want to display a UIAlertController on top of a UIViewController with a UICollectionView inside. The collection view needs to focus on startup, so I overrode the preferredFocusableView variable as follows:

override var preferredFocusedView: UIView? {
    return self.collectionView
}

With tvOS 9 all worked fine: the alert controller opened properly and I was able to choose one of the UIAlertActions displayed.

On tvOS 10 Golden Master, after opening the alert controller and scrolling to another action, the focus disappears from the screen and I'm unable to scroll to other actions or tapping on the Menu button of the Siri Remote. The app remains stuck in the alert controller and I can hear the scrolling sound when I try to scroll to other actions but nothing happens on screen. I have to force quit the app and reopen it.

enter image description here enter image description here

This is the code of the app. I tried to set the preferredFocusableView to alertController.preferredFocusedView or by removing the focus methods of the collection view but with no results.

var alertController : UIAlertController?

func showAlert() {

    alertController = UIAlertController(title:"Example title", message: "Example description", preferredStyle: .Alert)

    let action1 = UIAlertAction(title: "Option 1", style: .Default) { (action : UIAlertAction) -> Void in
        //call to another method
    }

    // action2, action3, action4...

    let action5 = UIAlertAction(title: "Option 5", style: .Default) { (action : UIAlertAction) -> Void in
        //call to another method
    }

    let actionDismiss = UIAlertAction(title: "Dismiss", style: .Destructive) { (action : UIAlertAction) -> Void in
        self.alertController!.dismissViewControllerAnimated(true, completion: nil)
    }

    alertController!.addAction(action1)
    alertController!.addAction(action2)
    alertController!.addAction(action3)
    alertController!.addAction(action4)
    alertController!.addAction(action5)
    alertController!.addAction(actionDismiss)

    alertController!.preferredAction = action1

    self.presentViewController(alertController!, animated: true, completion: nil)
}

override var preferredFocusedView: UIView? {
    if self.alertController != nil {
        return self.alertController!.preferredFocusedView
    } else {
        return self.collectionView
    }
}
Daniel Storm
  • 15,870
  • 6
  • 74
  • 133
Nicola Giancecchi
  • 2,905
  • 2
  • 21
  • 38

2 Answers2

4

Apple just replied to my radar:

Inside the attached application, you’re overwriting the functionality of UIScrollView in an extension to return true for canBecomeFocused(), which is the cause of these unexpected side effects. The focus seems to be disappearing when moving to a second UIAlertController option; however, it is actually transferring focus to the scroll view wrapped around the various components of the UIAlertController, since this is now allowed due to the extension mentioned above.

To solve this, create a custom subclass of UIScrollView to be used only in the instances where canBecomeFocused() must return true.

Nicola Giancecchi
  • 2,905
  • 2
  • 21
  • 38
1

You're overriding the entire systems focus engine. Try this:

// MARK: - Update Focus Helper
var viewToFocus: UIView? = nil {
    didSet {
        if viewToFocus != nil {
            self.setNeedsFocusUpdate()
            self.updateFocusIfNeeded()
        }
    }
}

override weak var preferredFocusedView: UIView? {
    if viewToFocus != nil {
        return viewToFocus
    } else {
        return super.preferredFocusedView
    }
}

Then set your alert as the view when you want the focus to change to it:

viewToFocus = someView
Daniel Storm
  • 15,870
  • 6
  • 74
  • 133
  • Still not working :( I'm setting the focus after the presentation of the UIAlertController. The two methods work correctly but the focus still disappear. – Nicola Giancecchi Sep 12 '16 at 13:08