Подтвердить что ты не робот

Анимация UIView, основанная на скорости UIPanGestureRecognizer

Я хотел бы иметь возможность перемещать субвью на экране и вне экрана так же, как вы просматриваете изображения в iPhone в приложении "Фото", поэтому, если subview больше, чем на 1/2 экрана, когда я отпускаю с моим палец он должен анимировать за пределами экрана, но он также должен поддерживать салфетки, поэтому, если скорость салфетки/панорамирования достаточно высока, она должна оживить экран, даже если он может быть меньше 1/2 экрана.

Моя идея состояла в том, чтобы использовать UIPanGestureRecognizer, а затем проверить скорость. Это работает, но как установить правильную продолжительность анимации для перемещения UIView на основе текущего местоположения и скорости панорамирования, чтобы он выглядел плавным? Если я установил фиксированное значение, анимация либо начнет замедляться, либо ускоряется по сравнению с скоростью прокрутки пальцев.

4b9b3361

Ответ 1

Документы говорят

Скорость движения жесты, которая выражается в точках в секунду. Скорость разбивается на горизонтальные и вертикальные компоненты.

Итак, я бы сказал, что если вы хотите переместить свой вид xPoints (измеренный в pt), чтобы он мог выйти из экрана, вы могли бы рассчитать продолжительность этого движения следующим образом:

CGFloat xPoints = 320.0;
CGFloat velocityX = [panRecognizer velocityInView:aView].x;
NSTimeInterval duration = xPoints / velocityX;
CGPoint offScreenCenter = moveView.center;
offScreenCenter.x += xPoints;
[UIView animateWithDuration:duration animations:^{
    moveView.center = offScreenCenter;
}];

Вместо этого вы можете использовать + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion, но и попробовать разные UIViewAnimationOptions.

Ответ 2

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

Если вы быстро катитесь и резко останавливаетесь, ждите и только затем завершаете жест (например, пользователь запускает салфетки, понимает, что это не то, что они хотели, поэтому они останавливаются, а затем отпускают палец), сообщает скорость от velocityInView: в состоянии UIGestureRecognizerStateEnded, когда ваш палец был выпущен, кажется, является быстрой скоростью, прежде чем я остановился и ждал, тогда как правая скорость в этом примере была бы нулевой (или почти нулевой). Короче говоря, сообщаемая скорость - это то, что было прямо перед концом кастрюли, но не скорость в конце самой кастрюли.

Я сам вычислил скорость самостоятельно. (Это кажется глупым, что это необходимо, но я не видел никакого способа обойти его, если бы я действительно хотел получить конечную скорость панорамирования.) Нижняя строка, когда состояние UIGestureRecognizerStateChanged, я отслеживаю текущий и предыдущий translationInView CGPoint, а также время, а затем используя эти значения, когда я был в UIGestureRecognizerStateEnded, чтобы вычислить фактическую конечную скорость. Это работает очень хорошо.

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

- (void)handlePanGesture:(UIPanGestureRecognizer *)gesture
{
    static CGPoint lastTranslate;   // the last value
    static CGPoint prevTranslate;   // the value before that one
    static NSTimeInterval lastTime;
    static NSTimeInterval prevTime;

    CGPoint translate = [gesture translationInView:self.view];

    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        lastTime = [NSDate timeIntervalSinceReferenceDate];
        lastTranslate = translate;
        prevTime = lastTime;
        prevTranslate = lastTranslate;
    }
    else if (gesture.state == UIGestureRecognizerStateChanged)
    {
        prevTime = lastTime;
        prevTranslate = lastTranslate;
        lastTime = [NSDate timeIntervalSinceReferenceDate];
        lastTranslate = translate;

        [self moveSubviewsBy:translate];
    }
    else if (gesture.state == UIGestureRecognizerStateEnded)
    {
        CGPoint swipeVelocity = CGPointZero;

        NSTimeInterval seconds = [NSDate timeIntervalSinceReferenceDate] - prevTime;
        if (seconds)
        {
            swipeVelocity = CGPointMake((translate.x - prevTranslate.x) / seconds, (translate.y - prevTranslate.y) / seconds);
        }

        float inertiaSeconds = 1.0;  // let calculate where that flick would take us this far in the future
        CGPoint final = CGPointMake(translate.x + swipeVelocity.x * inertiaSeconds, translate.y + swipeVelocity.y * inertiaSeconds);

        [self animateSubviewsUsing:final];
    }
}

Ответ 3

В большинстве случаев настройка UIViewAnimationOptionBeginFromCurrentState для аниматора UIView достаточно для безупречно продолженной анимации.