ObjectiveC: How to show a custom UIView on top of Unity

Hi!

I'd like to show a custom view on top of the Unity UIWindow (the main application window).

Here's the code for showing and hiding the view:

   - (void) showCustomView
    {
        UnitySetAudioSessionActive(true);
        UnityPause(true);

        self._customUIViewController = [[UIViewController alloc] autorelease];
        [[self getTopApplicationWindow] addSubview:self._customUIViewController.view];

        if(self._customUIViewController != nil)
        {       
            FAWController *fawView = [[FAWController alloc] initWithAPIKey:@"d0d" delegate:self]; //some custom initialization
            [self._customUIViewController presentModalViewController:fawView animated:YES];
            [fawView release];
        }
    }

    -(void)hideCustomView {

        if(self._customUIViewController != nil)
        {       
            [self._customUIViewController dismissModalViewControllerAnimated:YES];      
        }
        [self._customUIViewController.view removeFromSuperview];

        self._customUIViewController = nil;

        UnityPause(false);
        UnitySetAudioSessionActive(true);
    }
    //helper function
    - (UIWindow*) getTopApplicationWindow
    {
        UIApplication* clientApp = [UIApplication sharedApplication];
        NSArray* windows = [clientApp windows];
        UIWindow* topWindow = nil;

        if (windows && [windows count] > 0)
            topWindow = [[clientApp windows] objectAtIndex:0];

        return topWindow;
    }

The View shows just fine, and it dismisses nicely when I close it, but then Unity doesn't receive any more input (as if something invisible is still being displayed on top).

I'm pretty new to Objective-C, and I'd appreciate any help on this, to dismiss the custom view properly.

Thanks a lot, Bogdan

I realize this is a really old thread, but there are still people watching it so I’ll add my .02 cents. The issue you’re seeing is because you are trying to present the view of one view controller in another view controller without using view controller containment.

What you need to do is get a reference to the topmost view controller, then add your custom view controller as a child of that controller. This informs the custom view controller that it’s view will be managed by another controller.

Below is a code block that gives you an idea of how it works. I have a class I created for Unity code to call for my plugin. This file consists of 2 files iOSPluginClient.h and iOSPluginClient.mm. These files are located in the unity directory Assets/Plugins/iOS. The code sample below is from the .mm file.

Here’s the code:

#import "iOSPluginClient.h"
#import <UIKit/UIKit.h>

#import "DemoViewController.h"

@interface iOSPluginClient()

// tracker variable for when we add a contained view controller
@property (nonatomic, strong) UIViewController *presentedController;

@end

@implementation iOSPluginClient


+(instancetype) singleton
{
    static id _sharedInstance = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^
                  {
                      _sharedInstance = [self new];
                  });
    return _sharedInstance;
}


-(void) showDemoView
{
    // if you displayed as a contained controller and removed it then self.presentedController will be nil
    if (self.presentedController == nil)
    {
        // either load as contained controller or as presented controller
        // change this to see both ways of displaying your content then remove
        // when you know what you want to use
        BOOL loadAsContained = true;
        
        // this view controller is defined in a storyboard, get a reference to the containing storyboard
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
        
        // instantiate the view controller from the storyboard
        DemoViewController *demo = [storyboard instantiateViewControllerWithIdentifier:@"DemoVC"];
        
        if (loadAsContained)
        {
            // add this view controller as a contained controller (child) of the presented view controller
            [self addContainedController:demo];
        }
        else
        {
            // if you don't want to display as a child, and instead want to present the view controller
            // on top of the currently presented controller then use this method instead of the previous one
            [[self getTopViewController] presentViewController:demo animated:YES completion:nil];
        }
    }
    else
    {
        [self removeContainedController:self.presentedController];
    }
}

// condensed version
- (UIWindow*) getTopApplicationWindow
{
    // grabs the top most window
    NSArray* windows = [[UIApplication sharedApplication] windows];
    return ([windows count] > 0) ? windows[0] : nil;
}


- (UIViewController*) getTopViewController
{
    // get the top most window
    UIWindow *window = [self getTopApplicationWindow];
    
    // get the root view controller for the top most window
    UIViewController *vc = window.rootViewController;
    
    // check if this view controller has any presented view controllers, if so grab the top most one.
    while (vc.presentedViewController != nil)
    {
        // drill to topmost view controller
        vc = vc.presentedViewController;
    }
    
    return vc;
}


-(void) addContainedController:(UIViewController*)controller
{
    // get a reference to the current presented view controller
    UIViewController *parent = [self getTopViewController];
    
    // notify of containment
    [controller willMoveToParentViewController:parent];
    
    // add content as child
    [parent addChildViewController:controller];
    
    // set frame of child content (for demo inset by 100px padding on all sides)
    controller.view.frame = CGRectMake(100.0, 100.0, parent.view.bounds.size.width - 200.0, parent.view.bounds.size.height - 200.0);
    
    // get fancy, lets animate in
    controller.view.alpha = 0.0;
    
    // add as subview
    [parent.view addSubview:controller.view];
    
    // animation duration
    CGFloat duration = 0.3;
    
    // animate the alpha in and bring top views to top
    [UIView animateWithDuration:duration
                     animations:^
     {
         controller.view.alpha = 1.0;
     }
                     completion:nil
     ];
    
    // set our tracker variable
    self.presentedController = controller;
}

-(void) removeContainedController:(UIViewController*)controller
{
    // if fade out our view here just because
    [UIView animateWithDuration:0.3
                     animations:^
     {
         controller.view.alpha = 0;
     }
                     completion:^(BOOL finished)
     {
         
         // inform the child it is being removed by passing nil here
         [controller willMoveToParentViewController:nil];
         
         // remove the view
         [controller.view removeFromSuperview];
         
         // remove view controller from container
         [controller removeFromParentViewController];
         
         // nil out tracker
         self.presentedController = nil;
     }];
}

}


@end


/*******************************************************
 */
#pragma mark -    C String Helpers
/*
 ********************************************************/


// Converts C style string to NSString
NSString* CreateNSString (const char* string)
{
    if (string)
        return [NSString stringWithUTF8String: string];
    else
        return [NSString stringWithUTF8String: ""];
}


NSArray* ExplodeNSStringFromCString (const char* string)
{
    if (string)
        return [[NSString stringWithUTF8String: string] componentsSeparatedByString:@","];
    else
        return [NSArray new];
}

// Helper method to create C string copy
char* MakeStringCopy (const char* string)
{
    if (string == NULL)
        return NULL;
    
    char* res = (char*)malloc(strlen(string) + 1);
    strcpy(res, string);
    return res;
}



extern "C"
{
// function definition, called from c# or javascript unity code
void displayDemo()
    {
        // calls the method to display our view controller over the unity controller
        [[iOSPluginClient singleton] showDemoView];
    }
      
}  // end of extern C block

The c# code I have to call my objective c code is in a file called TestPlugin.cs. The code from that file looks as follows:

using UnityEngine;
// We need this one for importing our IOS functions
using System.Runtime.InteropServices;

public class TestPlugin : MonoBehaviour
{
    // Use this #if so that if you run this code on a different platform, you won't get errors.
	#if UNITY_IPHONE

	// For the most part, your imports match the function defined in the iOS code    
	[DllImport ("__Internal")]
	private static extern string showDemoView();

	#endif

	// Now make c# methods that can provide the iOS functionality
	static void ShowMyCustomView()
	{
	    // We check for UNITY_IPHONE again so we don't try this if it isn't iOS platform.
	#if UNITY_IPHONE
	    // Now we check that it's actually an iOS device/simulator, not the Unity Player. You only get plugins on the actual device or iOS Simulator.
	    if (Application.platform == RuntimePlatform.IPhonePlayer)
	    {
                // this calls our native method defined above
	        showDemoView();
	    }
	#endif
	}

void Update ()
	{
                // for demo purposes, tap the screen to present the view controller
		if (Input.GetMouseButtonDown(0)) 
		{
                        // call unity function that triggers call to native iOS code
			ShowMyCustomView();
		} 
	}





}

I had the similar task, and this post was very useful for me http://forum.unity3d.com/threads/56755-Unity-plugin-to-handle-loading-native-Cocoa-UI-s-with-ease

on the -(void)hideCustomView function set dismissModalViewControllerAnimated property to NO instead of YES, it works fine. Unity receives input fine.

I also have the same issue. I’ve tried setting the dismiss modal view : no but it doesn’t work. Anyone has a solution ?