CocosSharp
CocosSharp copied to clipboard
Allow to control "ControllerUserInteractionEnabled" on tvOS
Apple's HIG requires that the menu button of the remote exits the game to the home screen if you are on the first screen of an app. This is achieved by
- Setting
GCEventViewController.ControllerUserInteractionEnabled = true
- Calling
base.PressesBegan()
when handling input - Afterwards, the property can be set to false again.
MonoGame handles this already correctly. IMHO it would not be the worst idea to derive from iOSGameViewController
(which is unfortunately not public in MonoGame). This would prevent reinventing the wheel. Otherwise, CocosSharp should provide its own GCEventViewContrller subclass which handles this.
At the moment I handle it like this (changed CCGameView.tvOS.cs):
public override void PressesBegan (NSSet<UIPress> presses, UIPressesEvent evt)
{
FillPressesCollection (presses);
if(this.UseDefaultBackButtonHandling && presses.AnyObject != null && presses.AnyObject.Type == UIPressType.Menu)
{
var eventController = UIApplication.SharedApplication.Delegate.GetWindow().RootViewController as GCEventViewController;
if(eventController != null)
{
eventController.ControllerUserInteractionEnabled = true;
}
base.PressesBegan (presses, evt);
if(eventController != null)
{
eventController.ControllerUserInteractionEnabled = false;
}
}
}
@Krumelur I think you are on the right track. In MG we only call base.PressesXXXX if we get a Menu press AND want to exit.. but that only really works if you View is derived from GCEventViewController.
Also it seemed that ControllerUserInteractionEnabled needed to be true for all the PressesBegin/Ended events, so we reset it to false when the app comes back into focus again.
@Krumelur I might need to see the code, but I'm not following your approach of moving this logic to the game view. As of CocosSharp 1.7.0.0-pre1, the setup is to essentially bring your own view controller and associate it with a CCGameView
. So with respect to tvOS, why can't you simply subclass GCEventViewController
and slap a CCGameView
onto it? As in, make sure the current ViewController is of type GCEventViewController
rather than simply UIViewController
. Then you can put the above logic within your subclassed view controller.
Yes, I suppose this is slightly more cumbersome than the current MG approach, but there's a few things to keep in mind. Firstly, the project templates can simply be updated to ensure the view controller is correctly setup when targeting tvOS. I think this is something that will have to be addressed not just for CocosSharp, but for all tvOS apps.
Secondly, what happens when a user wants a multiple tabbed tvOS application? With the MG setup of taking over the entire application, this simply isn't possible. This was precisely the motivation for us to bring our interface up to the View level, to give developers control of the layout of their native application and the freedom to attach their game view where ever they see fit.
@rtabbara Yes, I can use my own GCEventViewController
and in fact, I do. But the question is: how much responsibility should I have as the user of CS and how much can CS do for me? If a tvOS app must exit if the menu button is clicked, I'd like to be able to handle this inside CS, without any native code. The problem I see is:
- CocosSharp allows me to detect the press of the menu button conveniently via an abstraction (some listener)
- This allows me to find out that "menu" has been pressed
- However, now it is already too late to handle the press natively:
PressesBegan()
has already been run when I get the callback - If I want to exit the game if "menu" is pressed,
PressesBegan()
would have to callbase.PressesBegan()
and seteventController.ControllerUserInteractionEnabled = true
(if I'm usingGCEventViewController
) to allow tvOS to handle the press and run the exit procedure (there is no way on tvOS to exit to the main menu directly - tvOS handles this internally if the press bubbles all the way up) - Problem 1: CS does not call base
- Problem 2: CS does not set
ControllerUserInteractionEnabled
, after all, it is not aware of theGVEventViewController
If we change CS to always call base, my only option is to override PressesBegan()
in my GCEventViewController
, do the detection of "menu" in there natively and handle it. But like I already said: why provide an abstraction and then we require users to use native code to handle the same thing again.
I'd like to see something where I can set a button click to a handled or not handled state. CS would tell me "menu was pressed!" and my code decides to return a value that indicates that I don't want to handle it but let the default behavior run instead. On tvOS this would let the event bubble up and exit my game.
Bottom line is: I'd really like Apple to provide a proper method to exit to the home screen instead of having the current behavior. If you google for this issue, you'll find that many Apps have been rejected because they don't exit properly. If so many people fail in using an API, the API is no good.
If we change CS to always call base, my only option is to override PressesBegan() in my GCEventViewController, do the detection of "menu" in there natively and handle it. But like I already said: why provide an abstraction and then we require users to use native code to handle the same thing again.
Because CCGameView
is not an abstraction. It's just a UIView
that happens to render game content. To me, it would be akin to requiring a UITableView
to handle the exit code. It just seems very strange.
But I understand the frustration. To me the crux of the issue is this requirement of having to toggle ControllerUserInteractionEnabled
. As you've said, this is a very bizarre setup.
We'll have to think a bit more carefully about how to do this right. Something along your suggestion of having a listener/event handler for when the menu-button is pressed seems to be a good compromise.