UIView бесконечный цикл Анимация для вызова метода после каждого цикла повторения - PullRequest
1 голос
/ 13 октября 2010

Я в своем уме пытаюсь придумать схему проектирования для этой парадигмы. Мне не очень повезло на этом сайте, но на этом этапе я попробую что-нибудь.

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

Когда радиус распространяется по окружности, если он пересекается с точкой (например, угол точки равен углу радиуса развертки + допуск), точка мигает.

Теперь я подошел к этой парадигме несколькими способами, но не нашел идеального решения. Вот что я попробовал.

ПЕРВЫЙ: блоки анимации iOS4. Я бы повернул радиус на 10 градусов за раз с продолжительностью .1 сек. И в методе завершения проверил пересечения с точками. Проблема здесь в том, что если вы установите опцию повтора, то метод завершения не вызывается. Единственный раз, когда вызывается метод завершения, это когда вся анимация завершается, а не после каждого цикла повторения, поэтому это решение не работает.

ВТОРОЕ: пробная явная анимация с использованием CABasicAnimation. Тот же подход, что и выше, с поворотом на 10 градусов каждые 0,1 секунды и установкой делегата на себя и реализацией метода animationDidFinish. В методе animationDidFinish я проверил пересечения с точкой. Я установил анимацию кумулятивным, а repeatCound - Huge Value. Рэдис вращается, но снова, animationDidFinish не вызывается, пока я не установлю для repeatCount небольшое значение, а затем он вызывается только в конце repeatCount, а не после каждого цикла повторения.

ТРЕТЬЕ: Использование NSTimer и этот подход действительно работает, но анимация может быть прерывистой в зависимости от того, что происходит вокруг экрана. Я использую отметку таймера 0,1 секунды и запускаю одиночную анимацию в 10 градусов, а также проверку пересечения на каждом отметке. Проблема этого подхода заключается в том, что он может зависнуть при запуске каждой анимации из-за того, что процессор выполняет другие анимации в фоновом режиме. Обратите внимание, что у меня не было этой проблемы с двумя другими подходами!

ЧЕТВЕРТЫЙ: Я пытался использовать комбинацию двух. CABasic Анимация по радиусу и NSTimer по точкам. Эта проблема заключается в том, что они могут выйти из синхронизации и довольно легко происходит, если iDevice переходит в режим сна, а затем возобновляет работу.

ПЯТЫЙ: использование анимационного блока в стиле iOS3.0 и поворот на 10 градусов с продолжительностью 0,1 сек. Установка делегата и animationDidStopSelector с помощью метода animationDidStop, вызывающего метод анимации снова, а также проверку пересечения с точками. Это тоже работает, но очень похоже на третье решение, оно дергается при прокрутке и других анимациях. Скорее всего, это вызвано природой анимации при останове.

В принципе, есть ли способ анимации представления бесконечно, но заставить его вызывать метод после каждого цикла повторения? Или есть способ заставить третье решение работать более плавно?

ПОЖАЛУЙСТА, ПОМОГИТЕ, Я ВЫПОЛНЯЮСЬ ИЗ КОНСТРУКЦИИ ДИЗАЙНА.

Ответы [ 3 ]

2 голосов
/ 17 октября 2010

Работает отлично.

Вот что я сделал:

1 / Применил объект поворота CABasicAnimation к слою для поворота на 360 градусов каждые 2 секунды.(не нужно делать это кумулятивным образом)

2 / Установить таймер для вызова уровня представления из слоя каждые .1 сек.

3 / Рассчитать угол по3D-преобразование хранится в слое презентации. Нажмите здесь , чтобы получить ответ о том, как это сделать.

4 / Проверьте наличие пересекающихся углов.

Выполнено: -)

Что следует остерегатьсяfor: 3D-преобразование основано на углах от 180 до -180 градусов, а не от 0 до 360 градусов. Используйте atan2, а не atan, поскольку более поздние будут давать углы только в первом квадранте.

1 голос
/ 19 декабря 2010

Хорошо, я прошел долгий путь с тех пор, как отправил этот вопрос.

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

Некоторые ключевые примечания: Создайте метод для проверки пересечения линии и точки:

-(BOOL)intersectWithAngle:(CGFloat)rad {        
    return (rad <= angle && rad >= angle - angleIncrement);
}

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

[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];

self.transform = rotationTransform;

[CATransaction commit];

Не нужно создавать новые преобразования каждый кадр, это просто расточительно и потребляет ресурсы. Лучше всего создать переменную / свойство экземпляра с именемtateTransform, изменить каждый кадр и повторно применить его к слою / представлению.

-(void)displayFrame {   
    rotationTransform.a = cos(angle);
    rotationTransform.b = sin(angle);
    rotationTransform.c = -sin(angle);
    rotationTransform.d = cos(angle);

    [CATransaction begin];
    [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];

    self.transform = rotationTransform;

    [CATransaction commit];
}

Затем создайте метод для фактической установки угла, используйте переменную / свойство экземпляра, чтобы сохранить угол и определить приращение угла (я использую 3 градуса), а также предварительно задать 2-пи, чтобы вы не всегда пересчитывали его .

-(void)processFrame {
    angle += angleIncrement;
    if (angle >= M_2PI) {
        angle -= M_2PI;
    }

    if ([self intersectWithAngle:point.angle]) {
        [point animate];
    } 
}

Наконец, создайте свой метод, который вызывается из CADisplayLink, который устанавливает фрейм и отображает его, поддерживая синхронизацию фрейма.

-(void)displayLinkDidTick {
    // get the time now
    NSTimeInterval timeNow = [NSDate timeIntervalSinceReferenceDate];

    if (timeOfLastDraw == 0) {
        numberOfTicks = 1;
    } else {
        NSTimeInterval timeSinceLastDraw = timeNow - timeOfLastDraw;
        NSTimeInterval desiredTimeInterval = kFrameDuration;

        numberOfTicks = (NSUInteger)(timeSinceLastDraw / desiredTimeInterval);      
    }

    if (numberOfTicks > 4) {
        numberOfTicks = 4;
        timeOfLastDraw = timeNow;
    } else {
        timeOfLastDraw += numberOfTicks * kFrameDuration;
    }


    while(numberOfTicks--) {

        [self processFrame];
        if (spriteState == SpriteStateIdle)
            break;
        }

    [self displayFrame];

}

Этот фрагмент кода был сильно изменен для этого поста, на самом деле я делаю анимацию линии сканирования и перелистывание точки в одном и том же экземпляре CADisplayLink.

0 голосов
/ 13 октября 2010

Интересно, сработает ли это:

UIView* yourView = ...;
// preparing the callbacks:
void(^)(void) animations = ...; // the animation would rotate 10 degrees
void(^)(BOOL) completion = ^(BOOL finished) {
  // do your radar stuff
  [UIView animateWithDuration:0.1 animations:animations completion:completion];
}

// the first call to start it all
[UIView animateWithDuration:0.1 animations:animations completion:completion];

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

...