Анимация изменения высоты и ширины в разное время

У меня есть изображение, которое я анимирую, чтобы оно выглядело так, как будто оно «дышит».

В настоящее время у меня есть изображение, перемещающееся приличным образом со следующим кодом ниже: (я анимирую UIView, который содержит несколько UIImageView, которые все движутся как одно целое)

- (IBAction)animateButton:(id)sender {


    [UIView animateWithDuration:0.64
                          delay:0
                        options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat
                     animations:^{
                         _testView.transform = CGAffineTransformMakeScale(1.08f, 1.02f);

                     } completion:nil];

}

ОДНАКО, я не могу понять, как анимировать растяжение изображения по x с другой скоростью, чем по y. Смысл этого в том, чтобы создать впечатление, что изображение на самом деле живое, а не циклично повторяющееся движение.

Я попытался привязать центр UIView к определенному месту, а затем добавить некоторое число к ширине с помощью анимации, скажем, 1,0 секунды. Затем я попытался одновременно вызвать другую анимацию, которая делает ту же анимацию только по высоте, с другим добавленным количеством, примерно на 1,3 секунды. Однако я не мог заставить эти два работать одновременно, так как одно имело бы приоритет над другим.

Если бы кто-то мог привести меня в правильном направлении, чтобы анимировать повторяющееся растяжение ширины и высоты с разной скоростью, я был бы очень признателен. Спасибо!


person Will Von Ullrich    schedule 19.09.2015    source источник


Ответы (1)


Учтите, что два перекрывающихся во времени изменения выглядят так:

        |---- change x ---|
   |---- change y ----|

Если два интервала произвольны и перекрываются, их можно представить тремя анимациями: одна изменяет одно измерение по отдельности, одна изменяет оба измерения вместе, а третья изменяет одно измерение по отдельности.

Как видите, существует множество способов указать это, но давайте попробуем самый простой. Чтобы избежать арифметики составных шкал, давайте укажем размер, изменение пикселя и продолжительность. Например...

@[ @{@"dimension":@"width", @"delta":@10, @"duration":0.2},
   @{@"dimension":@"both", @"delta":@40, @"duration":0.8}, 
   @{@"dimension":@"width", @"delta":@10, @"duration":0.2} ]

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

Нам нужен метод анимации, который будет выполнять изменения последовательно. Простой способ сделать это — рассматривать массив действий как список дел. Рекурсивный алгоритм говорит: чтобы сделать список вещей, сделайте первую, потом остальные....

- (void)animateView:(UIView *)view actions:(NSArray *)actions completion:(void (^)(BOOL))completion {
    if (actions.count == 0) return completion(YES);
    NSDictionary *action = actions[0];
    NSArray *remainingActions = [actions subarrayWithRange:NSMakeRange(1, actions.count-1)];
    [self animateView:view action:action completion:^(BOOL finished) {
        [self animateView:view actions:remainingActions completion:completion];
    }];
}

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

- (void)animateView:(UIView *)view action:(NSDictionary *)action completion:(void (^)(BOOL))completion {
    NSString *dimension = action[@"dimension"];
    CGFloat delta = [action[@"delta"] floatValue];
    NSTimeInterval duration = [action[@"duration"] floatValue];

    CGRect frame = view.frame;

    if ([dimension isEqualToString:@"width"]) {
        frame = CGRectInset(frame, -delta, 0);
    } else if ([dimension isEqualToString:@"height"]) {
        frame = CGRectInset(frame, 0, -delta);
    } else {
        frame = CGRectInset(frame, -delta, -delta);
    }
    [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
        view.frame = frame;
    } completion:completion];
}

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

person danh    schedule 20.09.2015
comment
спасибо за подробный ответ - я вмешивался в подобные теории, но у меня возникли проблемы с его точным выполнением. надеюсь, это приведет меня в правильном направлении в конце концов. - person Will Von Ullrich; 21.09.2015
comment
Если бы это был я, я бы поместил это в свой собственный небольшой проект (или, по крайней мере, в собственный контроллер представления в вашем текущем проекте) и просто дурачился с комбинациями. Сначала я использовал очень большие длительности, чтобы видеть, что происходит. - person danh; 21.09.2015