Subclassing MKAnnotationView and overriding setDragState - iphone

This is about an iPhone App using MKMapKit:
I created a custom MKAnnotationView for a draggable Annotation. I want to create a custom animation. I set a custom pin image and the annotation is draggable (which both is not shown here, it happens in the mapview) with the following code:
- (void) movePinUpFinished {
[super setDragState:MKAnnotationViewDragStateDragging];
[self setDragState:MKAnnotationViewDragStateDragging];
}
- (void) setDragState:(MKAnnotationViewDragState) myState {
if (myState == MKAnnotationViewDragStateStarting) {
NSLog(#"starting");
CGPoint endPoint = CGPointMake(self.center.x,self.center.y-20);
self.center = endPoint;
[self movePinUpFinished];
}
if (myState == MKAnnotationViewDragStateEnding) {
NSLog(#"ending");
[super setDragState:MKAnnotationViewDragStateEnding];
[self setDragState:MKAnnotationViewDragStateNone];
[super setDragState:MKAnnotationViewDragStateNone];
}
if (myState == MKAnnotationViewDragStateDragging) {
NSLog(#"dragging");
}
if (myState == MKAnnotationViewDragStateCanceling) {
NSLog(#"cancel");
}
if (myState == MKAnnotationViewDragStateNone) {
NSLog(#"none");
}
}
Everything works fine, the annotation is moved up a bit, is draggable and when i release the annotation, the mapview receives the "dragstateending".
But now I want the animation to run over a time period and change the dragStateStarting to the following:
if (myState == MKAnnotationViewDragStateStarting) {
NSLog(#"starting");
CGPoint endPoint = CGPointMake(self.center.x,self.center.y-20);
[UIView animateWithDuration:1.0
animations:^{ self.center = endPoint; }
completion:^(BOOL finished){ [self movePinUpFinished]; }];
}
The animations runs as wanted over the period of a second and the annotation is draggable. But when I release the annotation, the mapview is not receiving the ending through the delegat. What I also recognized was that when I am doing the animation with "UIView animateWithDuration..." is that immedently after beginning the dragging, as the animation starts, the ballon of the annotation opens. When i am setting the new center without the animation, the balloon keeps closed and is only opened after finishing the dragging by releasing the annotation.
What am I doing wrong? Is this the right way to override setDragState. Do I really have to call the super class? But without setting the dragstate in the superclass my mapview didnt realized the changes of the dragstate.
I wonder about the original implementation of MKPinAnnotationView, but because it is an internal Class I couldn't find a description of the setDragState method.
Thx for help. Cheers,
Ben

I had the pin drag working but was trying to figure out why the pin annimations that occur when you don't override setDragState - no longer work in my implementation. Your question contained my answer .. Thanks!
Part of the problem with your code is that once you override the setDragState function, per the xcode documentation, you are responsible for updating the dragState variable based on the new state coming in. I would also be a little concerned about your code calling itself (setDragState calling [self setDragState]).
Here is the code I ended up (with your help) that does all of the lifts, drags and drops as I expect them to occur. Hope this helps you too!
- (void)setDragState:(MKAnnotationViewDragState)newDragState animated:(BOOL)animated
{
if (newDragState == MKAnnotationViewDragStateStarting)
{
// lift the pin and set the state to dragging
CGPoint endPoint = CGPointMake(self.center.x,self.center.y-20);
[UIView animateWithDuration:0.2
animations:^{ self.center = endPoint; }
completion:^(BOOL finished)
{ self.dragState = MKAnnotationViewDragStateDragging; }];
}
else if (newDragState == MKAnnotationViewDragStateEnding)
{
// save the new location, drop the pin, and set state to none
/* my app specific code to save the new position
objectObservations[ACTIVE].latitude = pinAnnotation.coordinate.latitude;
objectObservations[ACTIVE].longitude = pinAnnotation.coordinate.longitude;
posChanged = TRUE;
*/
CGPoint endPoint = CGPointMake(self.center.x,self.center.y+20);
[UIView animateWithDuration:0.2
animations:^{ self.center = endPoint; }
completion:^(BOOL finished)
{ self.dragState = MKAnnotationViewDragStateNone; }];
}
else if (newDragState == MKAnnotationViewDragStateCanceling)
{
// drop the pin and set the state to none
CGPoint endPoint = CGPointMake(self.center.x,self.center.y+20);
[UIView animateWithDuration:0.2
animations:^{ self.center = endPoint; }
completion:^(BOOL finished)
{ self.dragState = MKAnnotationViewDragStateNone; }];
}
}

While Brian's solution worked, it lacked taking into account the users finger blocking the annotation view which is being manipulated.
This means that the user could not precisely place the pin once he was dragging it. The standard MKPinAnnotationView does a great job at this, what happens is when the finger begins dragging, the pin is lifted above the finger, and the visual point of the pin is used for placement not the previous centre point which now resides under the finger.
In addition to this my implementation also adds another animation when dropping the pin after dragging, by lifting the pin and dropping it with a higher speed. This is very close the the native user experience and will be apreciated by your users.
Please check out my gist on GitHub for the code.
What's really cool is setting a delegate is optional, optionally a notification is sent when the annotation view is dropped back onto the map.

I didn't study Ben's code much but it didn't worked for me. So I tried Brian's and it works great. Thanks a lot! I've been trying to solve annotation's animation during drag'n'drop for a long time.
But I have one suggestion to Brian's solution. I think that it would be better to support MKMapKit's delegate and notify it about changing dragState and save new position within standard delegate's method: - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)annotationView didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState. Here's my code:
DraggableAnnotationView.h:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface DraggableAnnotationView : MKAnnotationView
{
id <MKMapViewDelegate> delegate;
MKAnnotationViewDragState dragState;
}
#property (nonatomic, assign) id <MKMapViewDelegate> delegate;
#property (nonatomic, assign) MKAnnotationViewDragState dragState;
#property (nonatomic, assign) MKMapView *mapView;
#end
DraggableAnnotationView.m:
#import "DraggableAnnotationView.h"
#import "MapAnnotation.h"
#implementation DraggableAnnotationView
#synthesize delegate, dragState, mapView;
- (void)setDragState:(MKAnnotationViewDragState)newDragState animated:(BOOL)animated
{
[delegate mapView:mapView annotationView:self didChangeDragState:newDragState fromOldState:dragState];
if (newDragState == MKAnnotationViewDragStateStarting) {
// lift the pin and set the state to dragging
CGPoint endPoint = CGPointMake(self.center.x,self.center.y-20);
[UIView animateWithDuration:0.2
animations:^{ self.center = endPoint; }
completion:^(BOOL finished)
{ dragState = MKAnnotationViewDragStateDragging; }];
} else if (newDragState == MKAnnotationViewDragStateEnding) {
// drop the pin, and set state to none
CGPoint endPoint = CGPointMake(self.center.x,self.center.y+20);
[UIView animateWithDuration:0.2
animations:^{ self.center = endPoint; }
completion:^(BOOL finished)
{ dragState = MKAnnotationViewDragStateNone; }];
} else if (newDragState == MKAnnotationViewDragStateCanceling) {
// drop the pin and set the state to none
CGPoint endPoint = CGPointMake(self.center.x,self.center.y+20);
[UIView animateWithDuration:0.2
animations:^{ self.center = endPoint; }
completion:^(BOOL finished)
{ dragState = MKAnnotationViewDragStateNone; }];
}
}
- (void)dealloc
{
[super dealloc];
}
#end

Related

How to complete interactive UIViewController transition?

I've been dabbling with the new iOS 7 custom transition API and looked through all the tutorials/documentation I could find but I can't seem to figure this stuff out for my specific scenario.
So essentially what I'm trying to implement is a UIPanGestureRecognizer on a view where I would swipe up and transition to a VC whose view would slide up from the bottom while the current view would slide up as I drag my finger higher.
I have no problem accomplishing this without the interaction transition, but once I implement the interaction (the pan gesture) I can't seem to complete the transition.
Here's the relevant code from the VC that conforms to the UIViewControllerTransitionDelegate which is needed to vend the animator controllers:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"Swipe"]) {
NSLog(#"PREPARE FOR SEGUE METHOD CALLED");
UIViewController *toVC = segue.destinationViewController;
[interactionController wireToViewController:toVC];
toVC.transitioningDelegate = self;
toVC.modalPresentationStyle = UIModalPresentationCustom;
}
}
#pragma mark UIViewControllerTransition Delegate Methods
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController: (UIViewController *)presented presentingController: (UIViewController *)presenting sourceController:(UIViewController *)source {
NSLog(#"PRESENTING ANIMATION CONTROLLER CALLED");
SwipeDownPresentationAnimationController *transitionController = [SwipeDownPresentationAnimationController new];
return transitionController;
}
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
NSLog(#"DISMISS ANIMATION CONTROLLER CALLED");
DismissAnimatorViewController *transitionController = [DismissAnimatorViewController new];
return transitionController;
}
- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator {
NSLog(#"Interaction controller for dimiss method caled");
return interactionController.interactionInProgress ? interactionController:nil;
}
NOTE: The interaction swipe is only for the dismissal of the VC which is why it's in the interactionControllerForDismissal method
Here's the code for the animator of the dismissal which works fine when I tap on a button to dismiss it:
#import "DismissAnimatorViewController.h"
#implementation DismissAnimatorViewController
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 1.0;
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
NSTimeInterval duration = [self transitionDuration:transitionContext];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
CGRect initialFrameFromVC = [transitionContext initialFrameForViewController:fromVC];
UIView *containerView = [transitionContext containerView];
CGRect screenBounds = [[UIScreen mainScreen] bounds];
NSLog(#"The screen bounds is :%#", NSStringFromCGRect(screenBounds));
toVC.view.frame = CGRectOffset(initialFrameFromVC, 0, screenBounds.size.height);
toVC.view.alpha = 0.2;
CGRect pushedPresentingFrame = CGRectOffset(initialFrameFromVC, 0, -screenBounds.size.height);
[containerView addSubview:toVC.view];
[UIView animateWithDuration:duration
delay:0
usingSpringWithDamping:0.6
initialSpringVelocity:0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
fromVC.view.frame = pushedPresentingFrame;
fromVC.view.alpha = 0.2;
toVC.view.frame = initialFrameFromVC;
toVC.view.alpha = 1.0;
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}
#end
Here's the code for the UIPercentDrivenInteractiveTransition subclass which serves as the interaction controller:
#import "SwipeInteractionController.h"
#implementation SwipeInteractionController {
BOOL _shouldCompleteTransition;
UIViewController *_viewController;
}
- (void)wireToViewController:(UIViewController *)viewController {
_viewController = viewController;
[self prepareGestureRecognizerInView:_viewController.view];
}
- (void)prepareGestureRecognizerInView:(UIView*)view {
UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
gesture.minimumNumberOfTouches = 1.0;
[view addGestureRecognizer:gesture];
}
- (CGFloat)completionSpeed {
return 1 - self.percentComplete;
NSLog(#"PERCENT COMPLETE:%f",self.percentComplete);
}
- (void)handleGesture:(UIPanGestureRecognizer*)gestureRecognizer {
// CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view.superview];
CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view.superview];
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateBegan:
// 1. Start an interactive transition!
self.interactionInProgress = YES;
[_viewController dismissViewControllerAnimated:YES completion:nil];
break;
case UIGestureRecognizerStateChanged: {
// 2. compute the current position
CGFloat fraction = fabsf(translation.y / 568);
NSLog(#"Fraction is %f",fraction);
fraction = fminf(fraction, 1.0);
fraction = fmaxf(fraction, 0.0);
// 3. should we complete?
_shouldCompleteTransition = (fraction > 0.23);
// 4. update the animation controller
[self updateInteractiveTransition:fraction];
NSLog(#"Percent complete:%f",self.percentComplete);
break;
}
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled: {
// 5. finish or cancel
NSLog(#"UI GESTURE RECOGNIZER STATE CANCELED");
self.interactionInProgress = NO;
if (!_shouldCompleteTransition || gestureRecognizer.state == UIGestureRecognizerStateCancelled) {
[self cancelInteractiveTransition];
NSLog(#"Interactive Transition is cancled.");
}
else {
NSLog(#"Interactive Transition is FINISHED");
[self finishInteractiveTransition];
}
break;
}
default:
NSLog(#"Default is being called");
break;
}
}
#end
Once again, when I run the code now and I don't swipe all the way to purposefully cancel the transition, I just get a flash and am presented with the view controller I want to swipe to. This happens regardless if the transition completes or is canceled.
However, when I dismiss via the button I get the transition specified in my animator view controller.
I can see a couple of issues here - although I cannot be certain that these will fix your problem!
Firstly, your animation controller's UIView animation completion block has the following:
[transitionContext completeTransition:YES];
Whereas it should return completion based on the result of the interaction controller as follows:
[transitionContext completeTransition:![transitionContext transitionWasCancelled]]
Also, I have found that if you tell the UIPercentDrivenInteractiveTransition that a transition is 100% complete, it does not call the animation controller completion block. As a workaround, I limit it to ~99.9%
https://github.com/ColinEberhardt/VCTransitionsLibrary/issues/4
I've created a number of example interaction and animation controllers here, that you might find useful:
https://github.com/ColinEberhardt/VCTransitionsLibrary
I had this same problem. I tried the fixes above and others, but nothing worked. Then I stumbled upon https://github.com/MrAlek/AWPercentDrivenInteractiveTransition, which fixed everything.
Once you add it to your project, just replace UIPercentDrivenInteractiveTransition with AWPercentDrivenInteractiveTransition.
Also, you have to set the animator before starting an interactive transition. In my case, I use the same class for UIViewControllerAnimatedTransitioning and UIViewControllerInteractiveTransitioning, so I just did it in init():
init() {
super.init()
self.animator = self
}

MkAnnotation drop animation iOS 6

I try to animate my custom MkAnnotation, it works great on iOS 5 but not on iOS 6. Here is my didAddAnnotationViews method :
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)annotationViews
{
NSTimeInterval delayInterval = 0;
for (MKAnnotationView *annView in annotationViews)
{
// Don't pin drop if annotation is user location
if ([annView.annotation isKindOfClass:[MKUserLocation class]]) {
continue;
}
// Check if current annotation is inside visible map rect, else go to next one
MKMapPoint point = MKMapPointForCoordinate(annView.annotation.coordinate);
if (!MKMapRectContainsPoint(self.mapView.visibleMapRect, point)) {
continue;
}
CGRect endFrame = annView.frame;
// Move annotation out of view
annView.frame = CGRectMake(annView.frame.origin.x, annView.frame.origin.y - self.view.frame.size.height, annView.frame.size.width, annView.frame.size.height);
[UIView animateWithDuration:1
delay:delayInterval
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
annView.frame = endFrame;
}
completion:^(BOOL finished){
if (isModal)
[self.mapView selectAnnotation:[[self.mapView annotations] objectAtIndex:0] animated:YES];
}];
delayInterval += 0.0625;
}
}
I made this method with some parts of code i found on internet. On iOS 5 the animation is perfect, but on iOS 6 pins are just appearing without any kind of animation. Setting the mapView delegate is the first thing I do on my viewDidLoad, and i've also tried to generate my annotations from viewDidAppear method , without success.
Any idea ?
Thanks.
EDIT : Solution found, I use the perform:withObject:afterDelay: method and it seems to work.
- (void)viewDidLoad
{
[super viewDidLoad];
mapView.delegate = self;
[self performSelector:#selector(addAnnotation) withObject:nil afterDelay:0.0];
}
- (void)addAnnotation
{
MapViewAnnotation *annotation = [[MapViewAnnotation alloc] initWithTitle:#"test" andCoordinate:CLLocationCoordinate2DMake(49.6, 6.2)];
[mapView addAnnotation:annotation];
}
I found a solution, look at the edit section.

In ECSlidingViewController, how to disable the top view from sliding off screen

So I'm trying to implement the ECSlidingViewController sample into my App.
github source for this
The only thing I'd like to do to modify it, is prevent the TopView from completely sliding off-screen before it changes the contained view, and instead just keep the TopView in place but update it's contained view with it's new view controller that was selected from the menu. The Facebook app's take on this is exactly what I want, in case that sounded confusing.
After looking around for a while within the project, I've determined that it definitely (and obviously) has something to do with this line of code in the ECSlidingViewController.h:
- (void)anchorTopViewOffScreenTo:(ECSide)side animations:(void(^)())animations onComplete:(void(^)())complete;
I looked at where this gets called in the .m:
- (void)anchorTopViewOffScreenTo:(ECSide)side{
[self anchorTopViewOffScreenTo:side animations:nil onComplete:nil];
}
- (void)anchorTopViewOffScreenTo:(ECSide)side animations:(void(^)())animations onComplete:(void(^)())complete
{
CGFloat newCenter = self.topView.center.x;
if (side == ECLeft) {
newCenter = -self.resettedCenter;
} else if (side == ECRight) {
newCenter = self.screenWidth + self.resettedCenter;
}
[self topViewHorizontalCenterWillChange:newCenter];
[UIView animateWithDuration:0.25f animations:^{
if (animations) {
animations();
}
[self updateTopViewHorizontalCenter:newCenter];
} completion:^(BOOL finished){
if (complete) {
complete();
}
_topViewIsOffScreen = YES;
[self addTopViewSnapshot];
dispatch_async(dispatch_get_main_queue(), ^{
NSString *key = (side == ECLeft) ? ECSlidingViewTopDidAnchorLeft : ECSlidingViewTopDidAnchorRight;
[[NSNotificationCenter defaultCenter] postNotificationName:key object:self userInfo:nil];
});
}];
}
This is where I'm guessing the animation is being told how to animate, but I don't understand how any of this could be telling it to move off-screen. Am I overlooking something as simple as replacing something to nil? Perhaps there's another value somewhere that I haven't found? This is my first question on StackOverflow, and though I'm new to Obj-C in general, I have a pretty decent grasp on how it works. So I'm hoping to receive at least a tip in the right direction. Thanks!
Just use the following method
- (void)anchorTopViewTo:(ECSide)side animations:(void (^)())animations onComplete:(void(^)())complete
instead of
- (void)anchorTopViewOffScreenTo:(ECSide)side animations:(void(^)())animations onComplete:(void(^)())complete
If it's just the bouncing that you want to remove, then instead of calling:
[self anchorTopViewOffScreenTo:side animations:nil onComplete:nil];
call directly:
[self.slidingViewController resetTopView];

Swallow touches, unless touching a child of my current layer

I am writing a pause menu using a CCLayer. I need the layer to swallow touches so that you cannot press the layer below, however I also need to be able to use the buttons on the pause layer itself.
I can get the layer to swallow touches, but the menu won't work either.
Here is my code:
pauseLayer.m
#import "PauseLayer.h"
#implementation PauseLayer
#synthesize delegate;
+ (id) layerWithColor:(ccColor4B)color delegate:(id)_delegate
{
return [[[self alloc] initWithColor:color delegate:_delegate] autorelease];
}
- (id) initWithColor:(ccColor4B)c delegate:(id)_delegate {
self = [super initWithColor:c];
if (self != nil) {
NSLog(#"Init");
self.isTouchEnabled = YES;
CGSize wins = [[CCDirector sharedDirector] winSize];
delegate = _delegate;
[self pauseDelegate];
CCSprite * background = [CCSprite spriteWithFile:#"pause_background.png"];
[self addChild:background];
CCMenuItemImage *resume = [CCMenuItemImage itemFromNormalImage:#"back_normal.png"
selectedImage:#"back_clicked.png"
target:self
selector:#selector(doResume:)];
resume.tag = 10;
CCMenu * menu = [CCMenu menuWithItems:resume,nil];
[menu setPosition:ccp(0,0)];
[resume setPosition:ccp([background boundingBox].size.width/2,[background boundingBox].size.height/2)];
[background addChild:menu];
[background setPosition:ccp(wins.width/2,wins.height/2)];
}
return self;
}
-(void)pauseDelegate
{
NSLog(#"pause delegate");
if([delegate respondsToSelector:#selector(pauseLayerDidPause)])
[delegate pauseLayerDidPause];
}
-(void)doResume: (id)sender
{
if([delegate respondsToSelector:#selector(pauseLayerDidUnpause)])
[delegate pauseLayerDidUnpause];
[self.parent removeChild:self cleanup:YES];
}
- (void)onEnter {
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES];
[super onEnter];
}
- (void)onExit {
[[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
[super onExit];
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
return YES;
}
-(void)dealloc
{
[super dealloc];
}
#end
why dont you just disable touches on the game layer?
like in the onEnter method disable the touches on the game layer..and onExit re enable them
something like
-onEnter{
gameLayer.isTouchEnabled=NO;
....
}
-onExit{
gameLater.isTouchEnabled=YES;
...
}
also you wont need CCTouchDispatcher
According to your code, the problem is the modal layer is swallowing events even if it's for the own children.
To solve this problem, you have to set the touch priority of the children even higher than the modal layer itself.
In other words, set the menu's touch priority value below modal layer's.
There are two solutions.
Simply override "CCMenu::registerWithTouchDispatcher()" method and set the priority higher from the beginning.
Change menu's touch priority using "setPriority" method of the touchDispatcher or "setHandlerPriority" of menu itself.
To achieve second solution, you have to pay attention to the timing.
"CCMenu::registerWithTouchDispatcher()" is called somewhere AFTER "onEnter" and "onEnterTransitionDidFinish".
So, use "scheduleOnce" or something like that.
Sample codes.
- (id) initWithColor:(ccColor4B)c delegate:(id)_delegate {
self = [super initWithColor:c];
if (self != nil) {
//your codes...... put CCMenu in instance
[self scheduleOnce:#selector(setMenuPriority:) delay:0];
}
return self;
}
- (void) setMenuPriority (ccTime)dt {
[[[CCDirector sharedDirector] touchDispatcher] setPriority:INT_MIN forDelegate:menu];
//priority "INT_MIN" because you set the layer's priority to "INT_MIN+1".
}
PS: My english is not so good, so if there are loose sentences, correction will be very pleased :)
The problem is, that the layer/node hierarchy is not considered when propagating touches.
The touches are handed from the touch handler with the smallest priority value to the ones with the highest.
The touches are not forwarded anymore, once one of the responders swallows the touch.
You can use an approach similar to CCMenu. CCMenu handles all touches and detects which CCMenuItemhas been selected, based on the position of these items.
If you implement this the same way, you let your PauseLayer handle and swallow all touches and use a seperate mechanism to determine which child element in your PauseLayer has been selected.
Example Implementation: CCMenu Subclass
I have implemented a solution in this repository:
https://github.com/Ben-G/MGWU-Widgets/tree/master/Projectfiles/Components/CCMenuBlocking
That component is a CCMenuSubclass that swallows all touches and does not forward them.
Example Implementation: CCNode
Here is a more general solution of a CCNode that swallows touches and only forwards them to its own children:
https://github.com/Ben-G/MGWU-Widgets/blob/master/Projectfiles/Components/Popup/PopUp.m

iPhone, Cocos2D - moving sprite left/right while touching screen

I'm new to Objective C and app development so please go easy on me!
I'm trying to make a basic game and need to move a sprite left or right continuously while the user's finger is on the screen - left side to go left, right to go right...
I'm trying to use update to repeat movements of a few pixels every 1/60th second. So far, this is what I have (and sorry about the formatting):
#import "GameplayLayer.h"
#implementation GameplayLayer
-(id)init {
self = [super init];
if (self != nil) {
CGSize screenSize = [CCDirector sharedDirector].winSize;
// enable touches
self.isTouchEnabled = YES;
blobSprite = [CCSprite spriteWithFile:#"blob.png"];
[blobSprite setPosition: CGPointMake(screenSize.width/2, screenSize.height*0.17f)];
ball = [CCSprite spriteWithFile:#"ball.png"];
[ball setPosition:CGPointMake(10, screenSize.height*0.75f)];
[self addChild:blobSprite];
[self addChild:ball];
[self schedule:#selector(update) interval:1.0f/60.0f];
}
return self;
}
-(void) update:(ccTime)dt{
if (_tapDownLeft == YES){
blobSprite.position.x==blobSprite.position.x-5;
}
if (_tapDownRight == YES){
blobSprite.position.x==blobSprite.position.x+5;
}
}
-(void) ccTouchesBegan:(UITouch*)touch withEvent: (UIEvent *)event{
CGPoint touchLocation = [touch locationInView:[touch view]];
touchLocation = [[CCDirector sharedDirector] convertToGL:touchLocation];
if (touchLocation.x > 400) {
if ((blobSprite.position.x+10)<460){
_tapDownRight = YES;
}
}
if (touchLocation.x < 200) {
if ((blobSprite.position.x-10>20)){
_tapDownLeft = YES;
}
}
else {
_tapDownLeft = NO;
_tapDownRight = NO;
}
}
-(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event{
_tapDownLeft = NO;
_tapDownRight = NO;
}
-(void) registerWithTouchDispatcher{
[[CCTouchDispatcher sharedDispatcher]addTargetedDelegate:self priority:0 swallowsTouches:YES];
}
#end
Am I on the right lines with this? At the moment it's giving me 'expression result unused' in update. Could anyone tell me what I'm missing? Any help would be greatly appreciated.
Thanks,
Patrick
i see a few things here:
not certain your selector will call update : #selector(update:)
I would not rely on dt being either exactly 1/60th of a second, nor being constant. I would favor defining a speed constant (in points per second) and compute the deltaX in points based on the desired speed and dt, at each update cycle.
I dont see a 'registerWithTouchDispatcher' call (i try to place them in onEnter and onExit) methods.
Somewhere in there, make certain you remove your children (either in dealloc, or better in a local cleanup method (dont forget to invoke [super cleanup]).
Remove the argument in the update function