Sunday, April 19, 2009

What a real swipe should be?

What a real swipe should be ?

Before we get the correct answer, let see what Apple’s engineer told us to do. Look at this video[Matt Drance:Advanced UIKit and Device Features@iPhone Tech Talk World Tour] and seek for 47:15 and you will see, Matt Drance told us to do like this:

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

    // “start” is an instance variable
    start = [[touches anyObject] locationInView:self];
}

//Slide sub-title: Analyze direction and speed

//-------------------------

#define HORIZ_DRAG_MIN 12

#define VERT_DRAG_MAX 4

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    CGPoint current = [[touches anyObject] locationInView:self];
    float deltaX = fabsf(current.x - start.x);
    float deltaY = fabsf(current.y - start.y);

    if ( deltaX >= HORIZ_DRAG_MIN  && deltaY <= VERT_DRAG_MAX)
    {

        // it’s a swipe
    }
    else
    {
        // it’s not a swipe
    }

OK, I have to say, when you first read this, it did sound reasonable to determine a serials touch events to be a swipe or not. But when you really test it, you will see, you are missing a serious scenario:

Touch Down –> Move to the right –> then Move to the left quickly(You even don’t need to be so quick.)

Or, you can do it vice versa. You will only get the swipe right result. That’s not we really wanted, didn’t we?

The problem lies in how we determine the swipe action. If you test the above scenario in UITableView which is automatically taking care of swipe for us, you will see the UITableView works pretty fine. When you move to the back direction, it does move the table view to the correction direction as we wanted. That means we haven’t caught the right swipe events. So, where is the problem? The problem is Matt was wrong, or at least he did not tell us the full image of a swipe event.

He did write that we need to analyze the direction and speed. The solution to this is analyze the speed between the current movement and the movement just happened right before current one.

As a conclusion, you need to use:

     moveSpeed = fabsf(deltaX / deltaTime);

Where deltaTime is the subtraction of 2 timestamp of 2 movement event. When this speed is over 100 pixels/second, you will can say, this is a swipe action. (100pixel/s is my judgment, you might choose higher or lower speed.)

 

Here is the full story.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    // Record the start point
    theSecondLastPosition = [touch locationInView:self];
    theSecondLastTime = touch.timestamp;
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint currentTouchPosition = [touch locationInView:self];
    float deltaX = currentTouchPosition.x - theSecondLastPosition.x;
    float deltaY = currentTouchPosition.y - theSecondLastPosition.y;
    moveSpeed = fabsf(deltaX / (touch.timestamp - theSecondLastTime));

    // If the touch event falls into following pattern, it appears to be a swipe
    if ( moveSpeed >= SWIPE_SPEED)
    {
        if (theSecondLastPosition.x < currentTouchPosition.x){
            swipeType = SWIPE_RIGHT;
        }
        else{
            swipeType = SWIPE_LEFT;
        }
    }
    else
    {
        // Process a non-swipe event.
        swipeType = NOT_A_SWIPE;
    }

    theSecondLastPosition = [touch locationInView:self];
    theSecondLastTime = touch.timestamp;
}

Wow~~~, enjoy your new swipe events!

1 comment:

  1. Hi Friend,

    This article helped me. Thanks for the wonderful job. Keep going.

    ReplyDelete