MonoGame: getting the underlying UIViewController/UIView on iOS (GamePlatform)

For an experiment I needed to touch the internals of MonoGame; more precisely, I required the UIView the current game was drawing on. To do that I checked out the source code of MonoGame and found that if I have a game instance;

  1. MyGame game = new MyGame();

It created an instance of GamePlatform which is an abstraction over the platform specific implementation platform, in this case iOSGamePlatform (to be found in the iOS directory of the MonoGame source tree).

Specifically, iOSGamePlatform has a ViewController method which returns a UIViewController which, as iOS developers know, has a .View property which returns the UIView.

Now the problem is that this GamePlatform (and also iOSGamePlatform) class is not available to you, the creator of games. The GamePlatform is an abstract class which you cannot use from the outside. The only way I could find the instance of this class which is used by the game is by querying game.Services.GetService(), but as one cannot reach GamePlatform, this is not an option.

As I could not find another way of getting to the required instance, I decided to use some reflection to do the trick and it worked quite well;

First, get the Dictionary which holds the instance. This is a private property:

  1. FieldInfo[] fields = bg.Services.GetType().GetFields(
  2.                 BindingFlags.NonPublic |
  3.                 BindingFlags.Instance);
  4. var services = (System.Collections.IDictionary)fields [0].GetValue (game.Services);

Using System.Collections.IDictionary makes for you not having to specify the generics in case you cannot use them.

Then find the proper instance from the services dictionary without actually having access to that physical class:

  1. foreach (var k in services.Keys) {
  2.     if (k.ToString().Contains("iOSGamePlatform")) {
  3.         // found
  4.     }
  6. }

And, as a last step, get the UIViewController we wanted to begin with:

  1. UIViewController controller = null;
  2. foreach (var k in services.Keys) {
  3.     // get the iOSGamePlatform
  4.     if (k.ToString().Contains("iOSGamePlatform")) {
  5.         var v = services [k];
  6.         PropertyInfo getController = v.GetType ().GetProperty ("ViewController");
  7.         controller = (UIViewController)getController.GetGetMethod ().Invoke (v, new object[]{ });
  8.         break;
  9.     }
  11. }

Now controller.View will have the UIView it is using to render the game which I can now freely use however I want. If there is a nicer method to do this, I could not find it so this hack will do for now.


Found a faster way which I initially overlooked:

  1. UIViewController controller = (UIViewController) game.Services.GetService<UIViewController>();

At least I got to teach people about reflection!

Comments are closed.