views:

146

answers:

2

I have created a basic unlock slider like you use to unlock the iPhone.

In my touchesEnded function, I use an animation to move the slider back to home.

The first time the user moves the slider (and doesn't actually hit the end to unlock), the animation works fine and the slider moves back to home.

However, the next time the user tries to move the slider (and touchesMoved gets called), the slider gets pushed offscreen (to the left) by the amount they had slid the slider before.

So, what's happening?

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  self.dragging = NO;
  self.startX = 0; 
  [UIView beginAnimations:nil context:nil];
  [UIView setAnimationDuration:0.5];
  self.unlock.frame = CGRectMake(0, 0, 75, 35);
  [UIView commitAnimations];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
  if (!self.dragging ) { return; }
  UITouch *touch = [[event allTouches] anyObject];
  CGPoint point = [touch locationInView:self];
  int offset = point.x - self.startX;
  NSLog(@"%i", offset);
  if (offset > (LENGTH-SIZE)) {
    offset = (LENGTH-SIZE);
  }
  CGAffineTransform myTransform = CGAffineTransformMakeTranslation(offset, 0.0);
  [self.unlock setTransform:myTransform];
  if (point.x >= (LENGTH-(SIZE-self.startX))) {
    NSLog(@"Slider reached end");
    [self.sliderView removeFromSuperview];
  }
}

EDIT

Here's the working function, cleaned up a bit, though it could be better (thanks to a couple answered questions on SO from one hacker). I'm going to blog the complete module since there doesn't seem to be a standard widget:

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

  if (!self.dragging ) { return; }

  UITouch *touch = [[event allTouches] anyObject];
  CGPoint point = [touch locationInView:self];  

  if (point.x >= (LENGTH-(SIZE-self.startX))) {
    NSLog(@"Slider reached end");
    [self.sliderView removeFromSuperview];
  }

  // offset based on where the user clicked the slider
  int offset = point.x - self.startX;
  if (offset > (LENGTH-SIZE)) {
    offset = (LENGTH-SIZE);
  }

  //move the slider
  CGRect oldFrame = self.unlock.frame;
  CGRect newFrame = CGRectMake(self.frame.origin.x+offset, self.frame.origin.y-5, oldFrame.size.width, oldFrame.size.height);
  self.unlock.frame = newFrame; 
}
A: 

You need to reset the view transform in your touchesEnded handler:

[self.unlock setTransform:CGAffineTransformIdentity];

Try that instead of animating the frame property.

Jason
That's how I originally set this up, and it works fine, but it doesn't have the nice animation back to home.
Andrew Johnson
+1  A: 

IN your previous question I didn't realize you were moving the slider by changing its transform. Mixing up motion by changing its frame and transform is going to be moderately complicated.

Maybe you could try doing something more like this:

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
  if (!self.dragging ) { return; }
  UITouch *touch = [[event allTouches] anyObject];
  CGPoint point = [touch locationInView:self];
  int offset = point.x - self.startX;
  NSLog(@"%i", offset);
  if (offset > (LENGTH-SIZE)) {
    offset = (LENGTH-SIZE);
  }

  CGRect oldFrame = self.frame;
  CGRect newFrame = CGRectMake(oldFrame.origin.x+offset, oldFrame.origin.y, oldFrame.size.width, oldFrame.size.height);
  self.frame = newFrame;

  if (point.x >= (LENGTH-(SIZE-self.startX))) {
    NSLog(@"Slider reached end");
    [self.sliderView removeFromSuperview];
  }
}

That moves the slider by modifying its frame, not its translation, which should cause it to interact better with the snap back animation. Alternatively, rather than resetting the frame in the implicit animator you could reset the translation there.

Louis Gerbarg
Thanks, this comment got it working. I had to tweak the code a bit, and I put a nasty integer literal in it. I posted my working code above, and will fix it further.
Andrew Johnson