UIView анимация на основе скорости UIPanGestureRecognizer - PullRequest
35 голосов
/ 14 ноября 2011

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

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

Ответы [ 3 ]

54 голосов
/ 15 ноября 2011

В документах написано

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

Так что я бы сказал, если бы вы захотели переместить ваше представление 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.

21 голосов
/ 02 мая 2012

Наблюдение за положением скорости в 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's 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 голосов
/ 31 августа 2014

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

...