I watched the C4 tutorial on adding a pan gesture to an object and animating it to return to its original position when the panning is finished. I'm trying to add this to three individual objects. I have it working with one object so far to move it and reset it to a CGPoint, but for it to work, I have to add the pan gesture to "self", not the object. For reference, I'm pretty much using the code from here:
http://www.c4ios.com/tutorials/interactionPanning
If I add the gesture to the object itself, sure, it pans around, but then it just leaves itself at the last touch location. However, I'm assuming that leaving the gesture on "self" will affect more than just the object I want to move, and I want to be able to move the three objects individually.
I'm using roughly the same modification to the "move" method that's used in the example:
-(void)move:(UIPanGestureRecognizer *)recognizer {
[character move:recognizer];
if (recognizer.state == UIGestureRecognizerStateEnded) {
[character setCenter: charStartOrigin];
}
}
And then a new method to spawn the object:
-(void)createCharacters {
character = [C4Shape ellipse:charStart];
[character addGesture:PAN name:@"pan" action:@"move:"];
[self.canvas addShape:character];
}
The example link you are working from is sneaky. Since I knew that there was only going to be one object on the canvas I knew I could make it look like I was panning the label. This won't work for multiple objects, as you have already figured out.
To get different objects to move independently, and recognize when they are done being dragged, you need to subclass the objects and give them their own "abilities".
To do this I:
- Subclass
C4Shape
- Add custom behaviour to the new class
- Create subclassed objects on the canvas
The code for each step looks like the following:
subclassing
You have to create a subclass that gives itself some behaviour. Since you're working with shapes I have done it this way as well. I call my subclass Character
, its files look like this:
Character.h
#import "C4Shape.h"
@interface Character : C4Shape
@property (readwrite, atomic) CGPoint startOrigin;
@end
I have added a property to the shape so that I can set its start origin (i.e. the point to which it will return).
Character.m
#import "Character.h"
@implementation Character
-(void)setup {
[self addGesture:PAN name:@"pan" action:@"move:"];
}
-(void)move:(UIGestureRecognizer *)sender {
if(sender.state == UIGestureRecognizerStateEnded) {
self.center = self.startOrigin;
} else {
[super move:sender];
}
}
@end
In a subclass of a C4 object, setup
gets called in the same way as it does for the canvas... So, this is where I add the gesture for this object. Setup gets run after new
or alloc
/init
are called.
The move:
method is where I want to override with custom behaviour. In this method I catch the gesture recognizer, and if it's state is UIGestureRecognizerStateEnded
then I want to animate back to the start origin. Otherwise, I want it to move:
like it should so I simply call [super move:sender]
which runs the default move:
method.
That's it for the subclass.
Creating Subclassed Objects
My workspace then looks like the following:
#import "C4WorkSpace.h"
//1
#import "Character.h"
@implementation C4WorkSpace {
//2
Character *charA, *charB, *charC;
}
-(void)setup {
//3
CGRect frame = CGRectMake(0, 0, 100, 100);
//4
frame.origin = CGPointMake(self.canvas.width / 4 - 50, self.canvas.center.y - 50);
charA = [self createCharacter:frame];
frame.origin.x += self.canvas.width / 4.0f;
charB = [self createCharacter:frame];
frame.origin.x += self.canvas.width / 4.0f;
charC = [self createCharacter:frame];
//5
[self.canvas addObjects:@[charA,charB,charC]];
}
-(Character *)createCharacter:(CGRect)frame {
Character *c = [Character new];
[c ellipse:frame];
c.startOrigin = c.center;
c.animationDuration = 0.25f;
return c;
}
@end
I have added a method to my workspace that creates a Character
object and adds it to the screen. This method creates a Character
object by calling its new
method (I have to do it this way because it is a subclass of C4Shape
), turns it into an ellipse with the frame I gave it, sets its startOrigin
, changes its animationDuration
.
What's going on with the rest of the workspace is this (NOTE: the steps are marked in the code above):
- I
#import
the subclass so that I can create objects with it
- I create 3 references to Character objects.
- I create a frame that I will use to build each of the new objects
- For each object, I reposition
frame
by changing its origin and then use it to create a new object with the createCharacter:
method I wrote.
- I add all of my new objects to the
canvas
.
NOTE: Because I created my subclass with a startOrigin
property, I am able within that class to always animate back to that point. I am also able to set that point from the canvas whenever I want.