Implementing Game Center with Sprite Kit - objective-c

I'm unable to implement Game Center into my SpriteKit game.
Most tutorials I've screen require a ViewDidLoad which my game does not have.
I am unable to find correct code to Authenticate my game, or it could be that I have the correct code but am placing it in the wrong area of my application.
Currently I have placed the Authentication in the initWithSize:
#import <GameKit/GameKit.h>
#interface GameScene ()
...
#implementation GameScene
...
-(id)initWithSize:(CGSize)size { /* Setup your scene here */
if (self = [super initWithSize:size]) {
self.anchorPoint = CGPointMake(0.5,0.5);
self.physicsWorld.contactDelegate = self;
...
...
**(I HAVE BEEN PLACING AUTHENTICATE CODE FOUND ON INTERNET HERE, BUT NOTHING FOUND TO BE WORKING)**
}
return self;
}
The below code is where I have a UILabel node called "Leaderboards" where I want my player to be able to click, and enable the user to login to game center and view the leaderboards, along with his own score.
(When clicked currently, it says "Game Center Unavailable, Player not signed in", and then after clicking OK, the user is unable to return to the game, but is left with the game screen frozen?)
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
//if fire button touched, bring the rain
if ([node.name isEqualToString:#"LBButtonNode"]) {
NSLog(#"leader boards opened");
GKLeaderboardViewController *leaderboardController = [[GKLeaderboardViewController alloc] init];
if (leaderboardController != NULL)
{
leaderboardController.timeScope = GKLeaderboardTimeScopeAllTime;
leaderboardController.leaderboardDelegate = self;
GKGameCenterViewController *gameCenterController = [[GKGameCenterViewController alloc] init];
if (gameCenterController != nil)
{
gameCenterController.viewState = GKGameCenterViewControllerStateLeaderboards;
UIViewController *vc = self.view.window.rootViewController;
[vc presentViewController: gameCenterController animated: YES completion:nil];
}
}
}
else if(!self.isStarted) {
[self start];
}
else if (self.isGameOver) {
[self clear];
}
else if (self.isStarted) {
[hero jump];
}
}
How do I correctly implement the Game Center feature using SpriteKit?

Related

Methods in another scene not called

The 'thePlay' gets presented but does not display the redColor background. It remains black. Can someone advice on what I am supposed to do.
More Clarification: All the scenes and quickMenu class are imported on each other
This method execute transition from this scene(gameScene) using a button in quickMenu class (subclass of SKNode)
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches){
if (self.gameEnded && quickMenu.touchable) {
SKNode *theNode = [quickMenu nodeAtPoint:[touch locationInNode:quickMenu]];
if ([theNode.name isEqualToString:#"theMenu"]){
SKScene *thePlay = [[SKScene alloc] initWithSize:self.size];
SKTransition *doors = [SKTransition doorsOpenHorizontalWithDuration:1.0];
[self.view presentScene:thePlay transition:doors];
}
}
}
}
this is the first method in another scene (thePlay scene),
-(void)didMoveToView:(SKView *)view
{
self.backgroundColor = [SKColor redColor];
NSLog(#"this method called");
}
I tried the init method, did not show the redColor background as well, as shown below...
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size]) {
self.backgroundColor = [SKColor redColor];
NSLog(#"init method called");
}
Am I missing something?

Perform SpriteKit Physics Body Collision/ Contact on touch End

I am having some physics body nodes. Sprite Kit immediately calls didbegincontact method. I want that method should be called when i release the touch so that it may perform actions when click was released instead of calling method immediately causing some action setting problems for me.
- (void)didBeginContact:(SKPhysicsContact *)contact
{ NSLog(#"%hhd", _touching);
if(_touching == NO)
return;
something here
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
_touching = YES;
NSLog(#"%hhd", _touching);
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
_touching = NO;
NSLog(#"%hhd", _touching);
something here
}
Set the global Variable, ie
BOOL _touching;
When you touch / release (touches ended and began) you set that var to YES / NO;
In the didbegincontact, use something like
if(_touching == YES) {
// what I want to happen when I am touching
}
else {
// i must not be touching so do this
}
Here is the basic set up - however I think its the game logic thats the issue, maybe think of a different way to solve your problem
#interface XBLMyScene()
#property (strong, nonatomic) SKNode *world;
#property (strong, nonatomic) SKSpriteNode *ball;
#property BOOL touching;
#end
#implementation XBLMyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.world = [SKNode node];
[self addChild:self.world];
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
self.physicsBody = [SKPhysicsBody bodyWithEdgeFromPoint:CGPointZero toPoint:CGPointMake(500, 0)];
self.ball = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(40, 40)];
self.ball.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(40, 40)];
self.ball.position = CGPointMake(200, 300);
[self.world addChild:self.ball];
self.touching = NO;
}
return self;
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.touching = YES;
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
self.touching = NO;
}
- (void) didSimulatePhysics
{
if (self.touching) {
NSLog(#"I am touching");
}
else {
NSLog(#"I am not touching");
}
}

cocos2d ccTintTo, implementing an endless changing color label

Instead of writing all those lines at the init method of HelloWorldLayer :
CCTintTo* tint1 = [CCTintTo actionWithDuration:2 red:255 green:0 blue:0];
CCTintTo* tint2 = [CCTintTo actionWithDuration:2 red:0 green:0 blue:255];
....
CCSequence* sequence = [CCSequence actions:tint1, tint2, nil];
[label runAction:sequence];
I tried to make the label change color forever but got stucked:
I don't know where to place the relavant commands+ dealing with the integers x,y,z
I tried to do the randomize process at the update method,but didn't have any access to the label, any ideas?
// HelloWorldLayer.h
// Essentials
//
// Created by Steffen Itterheim on 14.07.10.
// Copyright Steffen Itterheim 2010. All rights reserved.
//
#import "cocos2d.h"
#interface HelloWorld : CCLayer
{
CCTintTo* tint1;
CCSequence* sequence1;
// CCLabelTTF* label; even tried property
}
// returns a Scene that contains the HelloWorld as the only child
+(id) scene;
#end
//
// HelloWorldLayer.m
// Essentials
//
// Created by Steffen Itterheim on 14.07.10.
// Copyright Steffen Itterheim 2010. All rights reserved.
//
#import "HelloWorldScene.h"
#import "MenuScene.h"
integer_t x;
integer_t y;
integer_t z;
#implementation HelloWorld
+(id) scene
{
CCScene* scene = [CCScene node];
CCLayer* layer = [HelloWorld node];
[scene addChild:layer];
return scene;
}
-(id) init
{
if ((self = [super init]))
{
CCLOG(#"init %#", self);
// enable touch input
self.isTouchEnabled = YES;
CGSize size = [[CCDirector sharedDirector] winSize];
// add the "touch to continue" label
CCLabelTTF* label = [CCLabelTTF labelWithString:#"Touch Screen For Awesome" fontName:#"AmericanTypewriter-Bold" fontSize:30];
label.position = CGPointMake(size.width / 2, size.height / 8);
[self addChild:label];
[self schedule:#selector(update:) interval:1/60.0f];
/*
tint1 = [CCTintTo actionWithDuration:2 red:x green:y blue:z];
sequence1 = [CCSequence actions:tint1, nil ];
id goaction=[CCRepeatForever actionWithAction:sequence1];
[label runAction:goaction];
*/
}
return self;
}
-(void) registerWithTouchDispatcher
{
// call the base implementation (default touch handler)
[super registerWithTouchDispatcher];
//[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES];
}
-(void) update:(ccTime)delta
{
x=(integer_t )(CCRANDOM_0_1()*255); y=(integer_t )(CCRANDOM_0_1()*255); z=(integer_t )(CCRANDOM_0_1()*255);
tint1 = [CCTintTo actionWithDuration:2 red:x green:y blue:z ];
sequence1 = [CCSequence actions:tint1, nil ];
[HelloWorld.label runAction:goaction]; //property label not found on object of type 'HelloWorld'
}
// Touch Input Events
-(CGPoint) locationFromTouches:(NSSet *)touches
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView: [touch view]];
return [[CCDirector sharedDirector] convertToGL:touchLocation];
}
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
}
-(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint location = [self locationFromTouches:touches];
CCLOG(#"touch moved to: %.0f, %.0f", location.x, location.y);
}
-(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// the scene we want to see next
CCScene* scene = [MenuScene scene];
CCTransitionSlideInR* transitionScene = [CCTransitionSlideInR transitionWithDuration:3 scene:scene];
[[CCDirector sharedDirector] replaceScene:transitionScene];
}
-(void) ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
}
-(void) dealloc
{
CCLOG(#"dealloc: %#", self);
// always call [super dealloc] at the end of every dealloc method
[super dealloc];
}
#end
If you want the tint color to be random for each time, then you cannot use CCTintTo directly inside CCRepeatForever. You need to re-randomize the RGB values for each CCTintTo action. Thus you need to embed the randomization process inside the action by using block. Here is how:
// do this in init method
__block void (^changeTint)(CCNode*) = [[^(CCNode *node) {
GLubyte x = (integer_t)(CCRANDOM_0_1()*255), y = (integer_t)(CCRANDOM_0_1()*255), z = (integer_t)(CCRANDOM_0_1()*255);
[node runAction:[CCSequence actionOne:[CCTintTo actionWithDuration:2 red:x green:y blue:z]
two:[CCCallBlockN actionWithBlock:changeTint]]];
} copy] autorelease];
changeTint(label);
You should look at CCRepeatForever action. As the name implies, it will repeat the action it points to forever. So you should remove your update method where you are changing the colors, and return to the CCSequence code that you had, and embed that in a CCRepeatForever action:
CCTintTo* tint1 = [CCTintTo actionWithDuration:2 red:255 green:0 blue:0];
CCTintTo* tint2 = [CCTintTo actionWithDuration:2 red:0 green:0 blue:255];
....
CCSequence* sequence = [CCSequence actions:tint1, tint2, nil];
CCAction* repeat = [CCRepeatForever actionWithAction:sequence];
[label runAction:repeat];

UIViewController stops receiving touch events on UIWebView

I am working on a magazine viewer and I have to use UIWebView for pages because of html5 interactive contents. First I tried uiwebviews in a UIScrollView but scrollview was too slow on sliding pages.
So now I am trying to write my own scrollview-like code. I have a MainView.xib, a view controller (Viewer) and extended UIWindow class (TouchCapturingWindow) that I take from here : http://wyldco.com/blog/2010/11/how-to-capture-touches-over-a-uiwebview/
when I try to slide pages fast, there is no problem but when I touch and slowly slide by not pulling my finger, "Viewer" view controller stops receiving touch events and so pages stop moving. I am logging TouchCapturingWindow, it still sending events. I searched many information and tutorials but I couldn't make it work. How can I make it continuously receive touch events?
I uploaded a simple Xcode project that contains only this part of my project. You can download here : http://testdergi.mysys.com/touchEvents.zip
When you run project, you should first download pages (180Kb) by tapping "Download Pages" button, then tap the "Open Viewer" button.
You can also look over the code below :
TouchCapturingWindow.h :
#interface TouchCapturingWindow : UIWindow {
NSMutableArray *views;
#private
UIView *touchView;
}
- (void)addViewForTouchPriority:(UIView*)view;
- (void)removeViewForTouchPriority:(UIView*)view;
#end
TouchCapturingWindow.m :
#implementation TouchCapturingWindow
- (void)dealloc {
}
- (void)addViewForTouchPriority:(UIView*)view {
if ( !views ) views = [[NSMutableArray alloc] init];
[views addObject:view];
}
- (void)removeViewForTouchPriority:(UIView*)view {
if ( !views ) return;
[views removeObject:view];
}
- (void)sendEvent:(UIEvent *)event {
//get a touch
UITouch *touch = [[event allTouches] anyObject];
//check which phase the touch is at, and process it
if (touch.phase == UITouchPhaseBegan) {
for ( UIView *view in views ) {
//if ( CGRectContainsPoint([view frame], [touch locationInView:[view superview]]) ) {
NSLog(#"TouchCapturingWindow --> TouchPhaseBegan");
touchView = view;
[touchView touchesBegan:[event allTouches] withEvent:event];
break;
}
}
else if (touch.phase == UITouchPhaseMoved) {
NSLog(#"TouchCapturingWindow --> TouchPhaseMoved");
if ( touchView ) {
[touchView touchesMoved:[event allTouches] withEvent:event];
}
else
{
NSLog(#"touch view is nil");
}
}
else if (touch.phase == UITouchPhaseCancelled) {
NSLog(#"TouchCapturingWindow --> TouchPhaseCancelled");
if ( touchView ) {
[touchView touchesCancelled:[event allTouches] withEvent:event];
touchView = nil;
}
}
else if (touch.phase == UITouchPhaseEnded) {
NSLog(#"TouchCapturingWindow --> TouchPhaseEnded");
if ( touchView ) {
[touchView touchesEnded:[event allTouches] withEvent:event];
touchView = nil;
}
}
//we need to send the message to the super for the
//text overlay to work (holding touch to show copy/paste)
[super sendEvent:event];
}
#end
Viewer.h :
#interface Viewer : UIViewController{
int currentPage;
int totalPages;
IBOutlet UIView *pagesView;
int lastTchX;
int difference;
BOOL hasMoved;
int touchBeganSeritX;
}
- (IBAction)backBtnClicked:(id)sender;
- (void)loadImages;
- (void)animationDidStop;
- (void)loadSinglePage:(int)pageNo;
#property (nonatomic, retain) IBOutlet UIView *pagesView;
#end
Viewer.m :
#interface Viewer ()
#end
#implementation Viewer
#synthesize pagesView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (IBAction)backBtnClicked:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
- (void)loadImages
{
for(UIView *subView in pagesView.subviews)
{
[subView removeFromSuperview];
}
currentPage = 1;
totalPages = 12;
for(int count = 1; count <= 12; count++)
{
[self loadSinglePage:count];
}
}
- (void)loadSinglePage:(int)pageNo
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory , NSUserDomainMask, YES);
NSString *cachesDir = [paths objectAtIndex:0];
NSString *pagesDir = [NSString stringWithFormat:#"%#/pages", cachesDir];
int pageX = (pageNo - 1) * 768;
UIWebView *aPageWebView = [[UIWebView alloc] init];
[aPageWebView setFrame:CGRectMake(pageX, 0, 768, 1024)];
aPageWebView.backgroundColor = [UIColor clearColor];
aPageWebView.opaque = YES;
[aPageWebView setClearsContextBeforeDrawing:YES];
aPageWebView.clipsToBounds = NO;
[aPageWebView setScalesPageToFit:YES];
NSString *hamData = [NSString stringWithFormat:#"<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"user-scalable=yes, width=1024, height=1365, maximum-scale=1.0\"><style type=\"text/css\">body {margin:0; padding:0;}</style></head><body bgcolor=\"#508CCF\"><div id=\"touchable\" style=\"top:0px; left:0px; width:1024px; height:1365px; background-image:url(%#.jpg)\"></div></body></html>", [[NSNumber numberWithInt:pageNo] stringValue]];
[aPageWebView loadHTMLString:hamData baseURL:[NSURL fileURLWithPath:pagesDir isDirectory:YES]];
aPageWebView.scrollView.bounces = NO;
[aPageWebView.scrollView setMaximumZoomScale:1.3333f];
[aPageWebView.scrollView setMinimumZoomScale:1.0f];
aPageWebView.scrollView.zoomScale = 1.0f;
[aPageWebView setMultipleTouchEnabled:YES];
[pagesView addSubview:aPageWebView];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touchesBegan");
UITouch *myTouch = [[event allTouches] anyObject];
int curTchX = [myTouch locationInView:self.view].x;
lastTchX = curTchX;
hasMoved = NO;
touchBeganSeritX = pagesView.frame.origin.x;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Viewer : .....moved");
hasMoved = YES;
UITouch *myTouch = [[event allTouches] anyObject];
int curTchX = [myTouch locationInView:self.view].x;
difference = curTchX - lastTchX;
int newX = (pagesView.frame.origin.x + difference);
if(newX <= 0)
{
[pagesView setFrame:CGRectMake((pagesView.frame.origin.x + difference), 0, pagesView.frame.size.width, 1024)];
}
lastTchX = curTchX;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touchesEnded");
if(hasMoved == YES)
{
hasMoved = NO;
int curSeritX = pagesView.frame.origin.x;
curSeritX = curSeritX / (-1);
int newX = 0;
if(difference < 0) //Sağa geçilecek
{
if((currentPage + 1) <= totalPages)
{
currentPage++;
}
}
else //Sola geçilecek
{
if((currentPage - 1) >= 1)
{
currentPage--;
}
}
newX = (currentPage - 1)*768*(-1);
[UIView animateWithDuration:0.2f
delay:0.0f
options:UIViewAnimationOptionCurveEaseOut
animations:^{
// Do your animations here.
[pagesView setFrame:CGRectMake(newX, 0, pagesView.frame.size.width, pagesView.frame.size.height)];
}
completion:^(BOOL finished){
if (finished) {
// Do your method here after your animation.
}
}];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touchesCancelled");
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
I am answering my own question. I found something after a hard work. I changed "(void)sendEvent:(UIEvent *)event" method like the code below. I saw that use of "[super sendEvent:event];" after UITouchPhaseMoved phase is problematic.
- (void)sendEvent:(UIEvent *)event {
//we need to send the message to the super for the
//text overlay to work (holding touch to show copy/paste)
//[super sendEvent:event];
//get a touch
UITouch *touch = [[event allTouches] anyObject];
//check which phase the touch is at, and process it
if (touch.phase == UITouchPhaseBegan) {
for ( UIView *view in views ) {
//if ( CGRectContainsPoint([view frame], [touch locationInView:[view superview]]) ) {
NSLog(#"TouchCapturingWindow --> TouchPhaseBegan");
touchView = view;
[touchView touchesBegan:[event allTouches] withEvent:event];
break;
}
[super sendEvent:event];
}
else if (touch.phase == UITouchPhaseMoved) {
NSLog(#"TouchCapturingWindow --> TouchPhaseMoved");
if ( touchView ) {
[touchView touchesMoved:[event allTouches] withEvent:event];
int curTchX = [touch locationInView:self].x;
NSLog(#"curTchX : %d", curTchX);
}
else
{
NSLog(#"touch view is nil");
}
}
else if (touch.phase == UITouchPhaseCancelled) {
NSLog(#"TouchCapturingWindow --> TouchPhaseCancelled");
if ( touchView ) {
[touchView touchesCancelled:[event allTouches] withEvent:event];
touchView = nil;
}
[super sendEvent:event];
}
else if (touch.phase == UITouchPhaseEnded) {
NSLog(#"TouchCapturingWindow --> TouchPhaseEnded");
if ( touchView ) {
[touchView touchesEnded:[event allTouches] withEvent:event];
touchView = nil;
}
[super sendEvent:event];
}
//we need to send the message to the super for the
//text overlay to work (holding touch to show copy/paste)
}
But the new problem is; if I add a video in a web page, and try to forward the video with its slider, page is moving. I have to find a way to detect if user taps on a video.

Observe touches on multiple views in one motion

I have a parent view with 3 separate child views. The child views are spread out within the parent with no overlap (and with some space in between). As a user moves her finger around the screen (without lifting it), I'd like to track touches as they enter and exit each of the child views.
Example: If the user begins touching somewhere on the screen outside of the child views, then swipes her finger over child 1, off of child 1, over child 2, and then lets go, I would expect these events to be triggered:
Touch began
Touch entered child 1
Touch exited child 1
Touch entered child 2
Touch ended
It seems as if touchesBegan:withEvent: and touchesEnded:withEvent: methods would be helpful in this case, but when I define them on the child view controllers, they don't do exactly what I want -- if the user begins touching outside the child view, then swipes over the child view, no touch events are triggered on the child itself.
Current Solution: I'm currently using a solution that feels really hacky to accomplish this. I'm observing touchesBegan:withEvent:, touchesEnded:withEvent:, and touchesMoved:withEvent: on the parent, grabbing the coordinates of each event, and determining if they lie within the bounds of a child. If they do, I trigger the appropriate events as described above.
This method mostly works, but feels very inefficient. It feels like the framework should handle this work for me. My state management code also sometimes misses an "enter" or "exit" trigger and I suspect it's because touch events were either dropped or came to me in an unexpected order. Am I missing a better method here?
The simplest solution would be something like:
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(pan:)];
[self.view addGestureRecognizer:pan];
// Do any additional setup after loading the view.
}
- (void)pan:(UIPanGestureRecognizer *)sender
{
static NSInteger startViewIndex;
static NSInteger endViewIndex;
CGPoint location = [sender locationInView:self.view];
if (sender.state == UIGestureRecognizerStateBegan)
{
if (CGRectContainsPoint(self.view0.frame, location))
startViewIndex = 0;
else if (CGRectContainsPoint(self.view1.frame, location))
startViewIndex = 1;
else if (CGRectContainsPoint(self.view2.frame, location))
startViewIndex = 2;
else
startViewIndex = -1;
}
else if (sender.state == UIGestureRecognizerStateEnded)
{
if (CGRectContainsPoint(self.view0.frame, location))
endViewIndex = 0;
else if (CGRectContainsPoint(self.view1.frame, location))
endViewIndex = 1;
else if (CGRectContainsPoint(self.view2.frame, location))
endViewIndex = 2;
else
endViewIndex = -1;
if (startViewIndex != -1 && endViewIndex != -1 && startViewIndex != endViewIndex)
{
// successfully moved between subviews!
NSLog(#"Moved from %1d to %1d", startViewIndex, endViewIndex);
}
}
}
Perhaps a little more elegant would be to define your own custom gesture recognizer (that way if you aren't dragging from one of your subviews, it will fail which will allow other gesture recognizers you might have going on elsewhwere to work ... probably not an issue unless you're use multiple gesture recognizers; it also isolates the gory details of the gesture logic from the rest of your view controller):
#interface PanBetweenSubviewsGestureRecognizer : UIPanGestureRecognizer
{
NSMutableArray *_arrayOfFrames;
}
#property NSInteger startingIndex;
#property NSInteger endingIndex;
#end
#implementation PanBetweenSubviewsGestureRecognizer
#synthesize startingIndex = _startingIndex;
#synthesize endingIndex = _endingIndex;
- (void)dealloc
{
_arrayOfFrames = nil;
}
- (id)initWithTarget:(id)target action:(SEL)action
{
self = [super initWithTarget:target action:action];
if (self)
{
_arrayOfFrames = [[NSMutableArray alloc] init];
}
return self;
}
- (void)addSubviewToArrayOfFrames:(UIView *)view
{
[_arrayOfFrames addObject:[NSValue valueWithCGRect:view.frame]];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self.view];
for (NSInteger i = 0; i < [_arrayOfFrames count]; i++)
{
if (CGRectContainsPoint([[_arrayOfFrames objectAtIndex:i] CGRectValue], location))
{
self.startingIndex = i;
return;
}
}
self.startingIndex = -1;
self.endingIndex = -1;
self.state = UIGestureRecognizerStateCancelled;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self.view];
for (NSInteger i = 0; i < [_arrayOfFrames count]; i++)
{
if (CGRectContainsPoint([[_arrayOfFrames objectAtIndex:i] CGRectValue], location))
{
self.endingIndex = i;
return;
}
}
self.endingIndex = -1;
self.state = UIGestureRecognizerStateCancelled;
}
#end
Which you could then use as follows:
- (void)viewDidLoad
{
[super viewDidLoad];
PanBetweenSubviewsGestureRecognizer *pan = [[PanBetweenSubviewsGestureRecognizer alloc] initWithTarget:self action:#selector(pan:)];
[pan addSubviewToArrayOfFrames:self.view0];
[pan addSubviewToArrayOfFrames:self.view1];
[pan addSubviewToArrayOfFrames:self.view2];
[self.view addGestureRecognizer:pan];
// Do any additional setup after loading the view.
}
- (void)pan:(PanBetweenSubviewsGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded && sender.startingIndex >= 0 && sender.endingIndex >= 0 && sender.startingIndex != sender.endingIndex)
{
// successfully moved between subviews!
NSLog(#"Moved from %1d to %1d", sender.startingIndex, sender.endingIndex);
}
}