iOS Input via UIView         This snippet provides an alternative to obtaining user input for iOS applications via UIView; additionally, it provides a strategy for interacting between C++/Objective-C instances/objects.



InputPimpl.h
#ifndef InputPimpl_h
#define InputPimpl_h

class InputPimpl
{
public:
    InputPimpl ( void );
    virtual ~InputPimpl( void );

    virtual void _notifyTapWasRecognized( float x0, float y0, uint numTouches
                                        , uint numTaps ) = 0;

    virtual void _notifyPanWasRecognized( float x0, float y0, uint numTouches
                                        , float xT, float yT
                                        , float xV, float yV ) = 0;

    virtual void _notifyPinchWasRecognized( float x0, float y0, uint numTouches
                                          , float scale, float velocity ) = 0;

protected:
    void _setupGestureInput( void );

private:
    void * self;
};

#endif

iOS_InputImpl.h
#ifndef iOS_InputImpl_h
#define iOS_InputImpl_h

#import "InputPimpl.h"
#import "iOS_InputVC.h"
#import <UIKit/UIKit.h>

////////////////////////////////////////////////////////////////////////

@interface iOS_InputImpl : NSObject <InputVCDelegate>
{
    InputPimpl * _pimpl;
}

- (id) initWithPimpl:(InputPimpl *)pimpl;

- (void) notifyTapWasRecognized:(TapGestureInfo *)info;
- (void) notifyPinchWasRecognized:(PinchGestureInfo *)info;
- (void) notifyPanWasRecognized:(PanGestureInfo *)info;

@end

#endif

iOS_InputImpl.mm
#import "InputPimpl.h"
#import "iOS_InputImpl.h"
#import "iOS_UIManager.h"

////////////////////////////////////////////////////////////////////////

@implementation iOS_InputImpl

////////////////////////////////////////////////////////////////////////

InputPimpl::InputPimpl( void )
    : self( NULL )
{   }

////////////////////////////////////////////////////////////////////////

InputPimpl::~InputPimpl( void )
{
    if ( self )
    {
        [(id)self dealloc];
        self = NULL;
    }
}

////////////////////////////////////////////////////////////////////////

void InputPimpl::_setupGestureInput( void )
{
    self = [[iOS_InputImpl alloc] initWithPimpl:this];
}

////////////////////////////////////////////////////////////////////////




////////////////////////////////////////////////////////////////////////

- (id) initWithPimpl:(InputPimpl *)pimpl
{
    _pimpl = pimpl;
    [[iOS_UIManager getInstance] enableInputView:YES withDelegate:self];
}

////////////////////////////////////////////////////////////////////////

- (void) dealloc
{
    [super dealloc];
}

////////////////////////////////////////////////////////////////////////

- (void) notifyTapWasRecognized:(TapGestureInfo *)info
{
    _pimpl->_notifyTapWasRecognized( info.pointBegan.x, info.pointBegan.y
                                   , info.numTouches,   info.numTaps    );
}

////////////////////////////////////////////////////////////////////////

- (void) notifyPanWasRecognized:(PanGestureInfo *)info
{
    _pimpl->_notifyPanWasRecognized( info.pointBegan.x,  info.pointBegan.y
                                   , info.numTouches
                                   , info.translation.x, info.translation.y
                                   , info.velocity.x,    info.velocity.y );
}

////////////////////////////////////////////////////////////////////////

- (void) notifyPinchWasRecognized:(PinchGestureInfo *)info
{
    _pimpl->_notifyPinchWasRecognized( info.pointBegan.x, info.pointBegan.y
                                     , info.numTouches,   info.scale
                                     , info.velocity  );
}

////////////////////////////////////////////////////////////////////////

@end

iOS_UIManager.h
#ifndef iOS_UIManager_h
#define iOS_UIManager_h

#import <UIKit/UIKit.h>

////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////


@interface iOS_UIManager : NSObject
{
@private
}

////////////////////////////////////////////////////////////////////////

@property (nonatomic, retain) UIWindow * window;

////////////////////////////////////////////////////////////////////////

+ (id) getInstance;

////////////////////////////////////////////////////////////////////////

- (void) enableInputView:(BOOL)value withDelegate:(id)theDelegate;

////////////////////////////////////////////////////////////////////////

@end

////////////////////////////////////////////////////////////////////////

#endif

iOS_UIManager.m
#import "iOS_UIManager.h"
#import "iOS_InputVC.h"


////////////////////////////////////////////////////////////////////////

typedef enum
{
    eVIEW_TAG_INPUT = 1
} EUIManagerViewTags;

////////////////////////////////////////////////////////////////////////


@implementation iOS_UIManager

@synthesize window;


////////////////////////////////////////////////////////////////////////

static iOS_UIManager * msInstance = nil;

+ (iOS_UIManager *) getInstance
{
    @synchronized(self)
    {
        if ( msInstance == nil )
        {
            msInstance = [[self alloc] init];
        }
    }
    return (msInstance);
}

////////////////////////////////////////////////////////////////////////

- (id) init
{
    if ( self = [super init] )
    {
        CGRect appRect = [[UIScreen mainScreen] applicationFrame];
        self.window = [[[UIWindow alloc] initWithFrame:appRect] autorelease];
        self.window.hidden = NO;
        self.window.backgroundColor = [UIColor clearColor];
    }
    return self;
}

////////////////////////////////////////////////////////////////////////

- (void) dealloc
{
    self.window = nil;
    [super dealloc];
}

////////////////////////////////////////////////////////////////////////

- (void) enableInputView:(BOOL)value withDelegate:(id)theDelegate
{
    if ( value )
    {
        iOS_InputVC * inputVC = [[iOS_InputVC alloc] initWithNibName:nil bundle:nil];
        inputVC.delegate = theDelegate;
        inputVC.view.tag = eVIEW_TAG_INPUT;
        [self.window setRootViewController:inputVC];
        [self.window addSubview:inputVC.view];
        [self.window bringSubviewToFront:inputVC.view];
        [self.window makeKeyAndVisible];
        [inputVC release];
    }
    else
    {
        UIView * inputView = [self.window viewWithTag:eVIEW_TAG_INPUT];
        if ( inputView )
        {
        [inputView removeFromSuperview];
        }
    }
}

////////////////////////////////////////////////////////////////////////

@end

////////////////////////////////////////////////////////////////////////

iOS_InputVC.h
#ifndef iOS_InputVC_h
#define iOS_InputVC_h

////////////////////////////////////////////////////////////////////////

@interface GestureInfo : NSObject { }
@property (nonatomic, assign) CGPoint    pointBegan;
@property (nonatomic, assign) NSUInteger numTouches;
@end

@interface TapGestureInfo : GestureInfo { }
@property (nonatomic, assign) NSUInteger numTaps;
@end

@interface PanGestureInfo : GestureInfo { }
@property (nonatomic, assign) CGPoint translation;
@property (nonatomic, assign) CGPoint velocity;
@end

@interface PinchGestureInfo : GestureInfo { }
@property (nonatomic, assign) CGFloat scale;
@property (nonatomic, assign) CGFloat velocity;
@end

////////////////////////////////////////////////////////////////////////

@protocol InputVCDelegate <NSObject>
@optional
- (void) notifyTapWasRecognized:(TapGestureInfo *)info;
- (void) notifyPanWasRecognized:(PanGestureInfo *)info;
- (void) notifyPinchWasRecognized:(PinchGestureInfo *)info;
@end

////////////////////////////////////////////////////////////////////////


@interface iOS_InputVC : UIViewController { }
@property (nonatomic, assign) id <InputVCDelegate> delegate;


////////////////////////////////////////////////////////////////////////

@end

////////////////////////////////////////////////////////////////////////
#endif

iOS_InputVC.mm
#import "iOS_InputVC.h"

#define SAFE_PERFORM_WITH_ARG(THE_OBJECT, THE_SELECTOR, THE_ARG)  \
    ((THE_OBJECT && [THE_OBJECT respondsToSelector:THE_SELECTOR]) \
    ?[THE_OBJECT performSelector:THE_SELECTOR withObject:THE_ARG] : nil)

////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
@interface iOS_InputVC()
- (void) enableTapRecognition;
- (void) enablePanRecognition;
- (void) enablePinchRecognition;
- (void) handleTapGesture:(UITapGestureRecognizer *)sender;
- (void) handlePanGesture:(UIPanGestureRecognizer *)sender;
- (void) handlePinchGesture:(UIPanGestureRecognizer *)sender;
@end
////////////////////////////////////////////////////////////////////////

@implementation GestureInfo
@synthesize pointBegan, numTouches;
@end
@implementation TapGestureInfo
@synthesize numTaps;
@end
@implementation PanGestureInfo
@synthesize translation, velocity;
@end
@implementation PinchGestureInfo
@synthesize scale, velocity;
@end

////////////////////////////////////////////////////////////////////////


@implementation iOS_InputVC

@synthesize delegate;


////////////////////////////////////////////////////////////////////////


- (void) dealloc
{
    self.delegate = nil;

    [super dealloc];
}

////////////////////////////////////////////////////////////////////////

- (void) loadView
{
    [super loadView];

    CGRect appRect = [[UIScreen mainScreen] applicationFrame];
    UIView * v = [[UIView alloc] initWithFrame: appRect];
    v.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    v.backgroundColor = [UIColor clearColor];
    v.opaque = YES;
    self.view = v;
    [v release];
}

////////////////////////////////////////////////////////////////////////

- (void) viewDidLoad
{
    [super viewDidLoad];

    [self enableTapRecognition];
    [self enablePanRecognition];
    [self enablePinchRecognition];
}

////////////////////////////////////////////////////////////////////////

- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
    return YES;
}

////////////////////////////////////////////////////////////////////////

- (void) enableTapRecognition
{
    UITapGestureRecognizer * tapGR = nil;

    // Add a recognizer that looks for the standard one-touch single tap.
    tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
    tapGR.numberOfTapsRequired	  = 1;
    tapGR.numberOfTouchesRequired = 1;
    [self.view addGestureRecognizer:tapGR];
    [tapGR release];

    // Add a recognizer that looks for the elusive four-touch triple tap.
    tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
    tapGR.numberOfTouchesRequired = 4;
    tapGR.numberOfTapsRequired    = 3;
    [self.view addGestureRecognizer:tapGR];
    [tapGR release];
}

////////////////////////////////////////////////////////////////////////

- (void) enablePanRecognition
{
    UIPanGestureRecognizer * panGR = nil;

    // Add a recognizer that looks for a one-touch panning (dragging) gesture.
    panGR = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
    [self.view addGestureRecognizer:panGR];
    [panGR release];
}

////////////////////////////////////////////////////////////////////////

- (void) enablePinchRecognition
{
    UIPinchGestureRecognizer * pinchGR = nil;

    // Add a recognizer that looks for two touches moving towards or away from one another.
    // (commonly used to zoom-out when moving together and zoom-in when moving away).
    pinchGR = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchGesture:)];
    [self.view addGestureRecognizer:pinchGR];
    [pinchGR release];
}

////////////////////////////////////////////////////////////////////////

- (void) handleTapGesture:(UITapGestureRecognizer *)sender;
{
    TapGestureInfo * info = [[TapGestureInfo alloc] init];
    info.pointBegan = [sender locationInView:self.view];
    info.numTouches = [sender numberOfTouches];
    info.numTaps	= [sender numberOfTapsRequired];

    SAFE_PERFORM_WITH_ARG( self.delegate, @selector(notifyTapWasRecognized:), info );

    [info release];
}

////////////////////////////////////////////////////////////////////////

- (void) handlePanGesture:(UIPanGestureRecognizer *)sender;
{
    PanGestureInfo * info = [[PanGestureInfo alloc] init];
    info.pointBegan  = [sender locationInView:self.view];
    info.numTouches  = [sender numberOfTouches];
    info.translation = [sender translationInView:self.view];
    info.velocity	 = [sender velocityInView:self.view];

    SAFE_PERFORM_WITH_ARG( self.delegate, @selector(notifyPanWasRecognized:), info );

    [info release];
}

////////////////////////////////////////////////////////////////////////

- (void) handlePinchGesture:(UIPinchGestureRecognizer *)sender;
{
    PinchGestureInfo * info = [[PinchGestureInfo alloc] init];
    info.pointBegan = [sender locationInView:self.view];
    info.numTouches = [sender numberOfTouches];
    info.scale		= [sender scale];
    info.velocity	= [sender velocity];

    SAFE_PERFORM_WITH_ARG( self.delegate, @selector(notifyPinchWasRecognized:), info );

    [info release];
}

////////////////////////////////////////////////////////////////////////

@end

////////////////////////////////////////////////////////////////////////

MyStateWithGestureSupport.h
#ifndef MyStateWithGestureSupport_h
#define MyStateWithGestureSupport_h

#include "State.hpp"
#include "InputPimpl.h"

class MyStateWithGestureSupport : public State
                                , private InputPimpl
{
public:
    MyStateWithGestureSupport ( void );
    virtual ~MyStateWithGestureSupport( void );

    DECLARE_STATE_CLASS( MyStateWithGestureSupport );

    void enter      ( void );
    void createScene( void );
    void exit       ( void );
            
    bool frameStarted        ( Ogre::FrameEvent const& evt );
    bool frameRenderingQueued( Ogre::FrameEvent const& evt ) { return true; }
    bool frameEnded          ( Ogre::FrameEvent const& evt ) { return true; }

    virtual void _notifyTapWasRecognized( float x0, float y0, uint numTouches
                                        , uint numTaps );
    
    virtual void _notifyPanWasRecognized( float x0, float y0, uint numTouches
                                        , float xT, float yT
                                        , float xV, float yV );
    
    virtual void _notifyPinchWasRecognized( float x0, float y0, uint numTouches
                                          , float scale, float velocity );
};

#endif

MyStateWithGestureSupport.cpp
#include "MyStateWithGestureSupport.h"
#include "Framework.hpp"

using namespace Ogre;

MyStateWithGestureSupport::MyStateWithGestureSupport( void )
    : _cubeEntity( NULL )
    , _cubeNode  ( NULL )
{   }

////////////////////////////////////////////////////////////////////

void MyStateWithGestureSupport::enter( void )
{
    Framework::getSingletonPtr()->m_pLog->logMessage( "\n~~ Entering MyStateWithGestureSupport ~~" );

    _setupGestureInput();
    createScene();

    Framework::getSingletonPtr()->m_pLog->logMessage( "~~ Entered MyStateWithGestureSupport ~~\n" );
}

////////////////////////////////////////////////////////////////////

void MyStateWithGestureSupport::createScene( void )
{
    _sceneMgr = Framework::getSingletonPtr()->m_pRoot->createSceneManager( ST_GENERIC, "GameSceneMgr" );
    _sceneMgr->setAmbientLight(Ogre::ColourValue(0.7f, 0.7f, 0.7f));

    _camera = _sceneMgr->createCamera( "GameCamera"  );
    _camera->setPosition( Vector3( 5.f, 60.f, 60.f ) );
    _camera->lookAt     ( Vector3( 5.f, 20.f,  0.f ) );
    _camera->setNearClipDistance ( 5.f );
    _camera->setAspectRatio( Real( Framework::getSingletonPtr()->m_pViewport->getActualWidth () ) /
                             Real( Framework::getSingletonPtr()->m_pViewport->getActualHeight() ) );

    Framework::getSingletonPtr()->m_pViewport->setCamera( _camera );

    _sceneMgr->setSkyBox( true, "Examples/SpaceSkyBox" );
    _sceneMgr->createLight( "Light" )->setPosition( 75.f, 75.f, 75.f );

    _cubeEntity = _sceneMgr->createEntity( "Cube", "ogrehead.mesh" );
    _cubeNode	= _sceneMgr->getRootSceneNode()->createChildSceneNode( "CubeNode" );
    _cubeNode->attachObject( _cubeEntity );
    _cubeNode->setPosition ( Vector3( 0.f, 0.f, -25.f ) );
}

////////////////////////////////////////////////////////////////////

bool MyStateWithGestureSupport::frameStarted( Ogre::FrameEvent const& evt)
{
    return true;
}

////////////////////////////////////////////////////////////////////

void MyStateWithGestureSupport::exit( void )
{
    if ( _sceneMgr )
    {
        if ( _camera ) { _sceneMgr->destroyCamera( _camera ); }
        Framework::getSingletonPtr()->m_pRoot->destroySceneManager( _sceneMgr );
    }
    Framework::getSingletonPtr()->m_pLog->logMessage( "~~ Left TestStateA ~~\n" );
}

////////////////////////////////////////////////////////////////////////

void MyStateWithGestureSupport::_notifyTapWasRecognized( float x0, float y0, uint numTouches
                                                       , uint numTaps )
{
    Framework::getSingletonPtr()->m_pLog->logMessage( "[TAP]" );
}

////////////////////////////////////////////////////////////////////////

void MyStateWithGestureSupport::_notifyPanWasRecognized( float x0, float y0, uint numTouches
                                                       , float xT, float yT
                                                       , float xV, float yV )
{
    Framework::getSingletonPtr()->m_pLog->logMessage( "[PAN]" );
}

////////////////////////////////////////////////////////////////////////

void MyStateWithGestureSupport::_notifyPinchWasRecognized( float x0, float y0, uint numTouches
                                                         , float scale, float velocity )
{
    Framework::getSingletonPtr()->m_pLog->logMessage( "[PINCH]" );
}

////////////////////////////////////////////////////////////////////////