Жест вращения двумя пальцами на iPhone? - PullRequest
2 голосов
/ 20 августа 2009

Я работаю над приложением для iPhone с множеством различных жестов, которые вы можете сделать. В настоящее время есть выбор / перетаскивание одним пальцем, прокрутка двумя пальцами и увеличение / уменьшение масштаба двумя пальцами. Я хочу добавить вращение двумя пальцами (ваши пальцы вращают точку между ними), но я не могу понять, как заставить его работать правильно. Все остальные жесты были линейными, так что они сводились только к использованию точечного или перекрестного произведения.

Я думаю, мне нужно сохранить наклон между двумя предыдущими точками каждого пальца, и если угол между векторами близок к 90, то есть вероятность вращения. Если угол перемещения следующего пальца также близок к 90, а направление вектора на одном пальце изменилось положительно и изменилось отрицательно, то вы получили вращение. Проблема в том, что мне нужно очень четкое различие между этим жестом и остальными, а вышесказанное недостаточно далеко.

Есть предложения?

РЕДАКТИРОВАТЬ: Вот как я это сделал в векторном анализе (в отличие от предложения ниже о соответствующих пикселях, обратите внимание, что я использую мою векторную структуру здесь, вы сможете угадать, что делает каждая функция):

//First, find the vector formed by the first touch's previous and current positions.
struct Vector2f firstChange = getSubtractedVector([theseTouches get:0], [lastTouches get:0]);
//We're going to store whether or not we should scroll.
BOOL scroll = NO;

//If there was only one touch, then we'll scroll no matter what.
if ([theseTouches count] <= 1)
{
    scroll = YES;
}
//Otherwise, we might scroll, scale, or rotate.
else
{
    //In the case of multiple touches, we need to test the slope between the two touches.
    //If they're going in roughly the same direction, we should scroll. If not, zoom.
    struct Vector2f secondChange = getSubtractedVector([theseTouches get:1], [lastTouches get:1]);

    //Get the dot product of the two change vectors.
    float dotChanges = getDotProduct(&firstChange, &secondChange);

    //Get the 2D cross product of the two normalized change vectors.
    struct Vector2f normalFirst = getNormalizedVector(&firstChange);
    struct Vector2f normalSecond = getNormalizedVector(&secondChange);
    float crossChanges = getCrossProduct(&normalFirst, &normalSecond);

    //If the two vectors have a cross product that is less than cosf(30), then we know the angle between them is 30 degrees or less.
    if (fabsf(crossChanges) <= SCROLL_MAX_CROSS && dotChanges > 0)
    {
        scroll = YES;
    }
    //Otherwise, they're in different directions so we should zoom or rotate.
    else
    {
        //Store the vectors represented by the two sets of touches.
        struct Vector2f previousDifference = getSubtractedVector([lastTouches  get:1], [lastTouches  get:0]);
        struct Vector2f currentDifference  = getSubtractedVector([theseTouches get:1], [theseTouches get:0]);

        //Also find the normals of the two vectors.
        struct Vector2f previousNormal = getNormalizedVector(&previousDifference);
        struct Vector2f currentNormal  = getNormalizedVector(&currentDifference );

        //Find the distance between the two previous points and the two current points.
        float previousDistance = getMagnitudeOfVector(&previousDifference);
        float currentDistance  = getMagnitudeOfVector(&currentDifference );

        //Find the angles between the two previous points and the two current points.
        float angleBetween = atan2(previousNormal.y,previousNormal.x) - atan2(currentNormal.y,currentNormal.x);

        //If we had a short change in distance and the angle between touches is a big one, rotate.
        if ( fabsf(previousDistance - currentDistance) <= ROTATE_MIN_DISTANCE && fabsf(angleBetween) >= ROTATE_MAX_ANGLE)
        {
            if (angleBetween > 0)
            {
                printf("Rotate right.\n");
            }
            else
            {
                printf("Rotate left.\n");
            }
        }
        else
        {
            //Get the dot product of the differences of the two points and the two vectors.
            struct Vector2f differenceChange = getSubtracted(&secondChange, &firstChange);
            float dotDifference = getDot(&previousDifference, &differenceChange);
            if (dotDifference > 0)
            {
                printf("Zoom in.\n");
            }
            else
            {
                printf("Zoom out.\n");
            }
        }
    }
}

if (scroll)
{
    prinf("Scroll.\n");
}

Вы должны заметить, что если вы просто делаете манипуляции с изображениями или выполняете прямое вращение / масштабирование, тогда вышеприведенный подход вполне подойдет. Однако, если вы похожи на меня и используете жест, чтобы вызвать что-то, что требует времени для загрузки, вероятно, вы захотите избегать выполнения действия, пока этот жест не будет активирован несколько раз подряд. Разница между каждым из них в моем коде все еще не является совершенно отдельной, поэтому иногда в ряде масштабов вы получаете вращение или наоборот.

Ответы [ 2 ]

3 голосов
/ 20 августа 2009

Я делал это раньше, находя предыдущее и текущее расстояния между двумя пальцами и угол между предыдущей и текущей линиями. Затем я выбрал некоторые эмпирические пороговые значения для этой дельты и угла тета, и это сработало для меня.

Если расстояние было больше моего порога, а угол был меньше моего порога, я масштабировал изображение. В противном случае я повернул его. Прокрутка двумя пальцами кажется легко различимой.

Кстати, если вы на самом деле сохраняете значения, штрихи уже сохраняют значения предыдущих точек.

CGPoint previousPoint1 = [self scalePoint:[touch1 previousLocationInView:nil]];
CGPoint previousPoint2 = [self scalePoint:[touch2 previousLocationInView:nil]];
CGPoint currentPoint1 = [self scalePoint:[touch1 locationInView:nil]];
CGPoint currentPoint2 = [self scalePoint:[touch2 locationInView:nil]];
2 голосов
/ 20 августа 2009

Два пальца, оба движущиеся, противоположные (иш) направления. Какой жест противоречит этому?

Пинч / зум, я думаю, подходит близко, но, в то время как пинч / зум начнут отходить от центральной точки (если вы проведете назад от каждой линии, ваши линии будут параллельными и близкими), у поворота первоначально будут параллельные линии трассировки назад), которые будут находиться далеко друг от друга, и эти линии будут постоянно менять наклон (при сохранении расстояния).

edit: Вы знаете - оба из них могут быть решены с помощью одного и того же алгоритма.

Вместо вычисления линий рассчитайте пиксель под каждым пальцем. Если пальцы двигаются, переведите изображение так, чтобы два начальных пикселя все еще находились под двумя пальцами.

Это решает все действия двумя пальцами, включая прокрутку.

Иногда прокрутка двумя пальцами или масштабирование могут показаться немного шаткими, поскольку они также будут выполнять другие операции, но именно так приложение карты работает (исключая поворот, которого у него нет).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...