Как связать CGAffineTransform вместе в отдельных анимациях?

Мне нужно две анимации на UIView:

  1. Заставьте представление двигаться вниз и слегка расти.
  2. Сделайте вид еще больше о своем новом центре.

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

введите описание изображения здесь

#import "ViewController.h"

static const CGFloat kStartX = 100.0;
static const CGFloat kStartY = 20.0;
static const CGFloat kStartSize = 30.0;
static const CGFloat kEndCenterY = 200.0;

@interface ViewController ()

@property (nonatomic, strong) UIView *box;

@end

@implementation ViewController

- (void)viewDidLoad
{
  [super viewDidLoad];

  self.box = [[UIView alloc] initWithFrame:CGRectMake(kStartX, kStartY, kStartSize, kStartSize)];
  self.box.backgroundColor = [UIColor brownColor];
  [self.view addSubview:self.box];

  [UIView animateWithDuration:2.0
                        delay:1.0
       usingSpringWithDamping:1.0
        initialSpringVelocity:0.0
                      options:0
                   animations:^{
                     self.box.transform = [self _transformForSize:50.0 centerY:kEndCenterY];
                   }
                   completion:^(BOOL finished) {
                     [UIView animateWithDuration:2.0
                                           delay:1.0
                          usingSpringWithDamping:1.0
                           initialSpringVelocity:0.0
                                         options:0
                                      animations:^{
                                        self.box.transform = [self _transformForSize:100.0 centerY:kEndCenterY];
                                      }
                                      completion:^(BOOL finished) {
                                      }];
                   }];
}

- (CGAffineTransform)_transformForSize:(CGFloat)newSize centerY:(CGFloat)newCenterY
{
  CGFloat newScale = newSize / kStartSize;
  CGFloat startCenterY = kStartY + kStartSize / 2.0;
  CGFloat deltaY = newCenterY - startCenterY;
  CGAffineTransform translation = CGAffineTransformMakeTranslation(0.0, deltaY);
  CGAffineTransform scaling = CGAffineTransformMakeScale(newScale, newScale);
  return CGAffineTransformConcat(scaling, translation);
}

@end

Есть одно предостережение: я вынужден использовать setTransform, а не setFrame. Я не использую коричневую коробку в моем реальном коде. Мой настоящий код использует сложный подкласс UIView, который не масштабируется плавно, когда я использую setFrame.

1 ответ

Решение

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

  [UIView animateWithDuration:2.0
                        delay:1.0
       usingSpringWithDamping:1.0
        initialSpringVelocity:0.0
                      options:0
                   animations:^{
                     self.box.transform = [self _transformForSize:50.0 centerY:kEndCenterY];
                   }
                   completion:^(BOOL finished) {
                     // <new code here>
                     CGRect newFrame = self.box.frame;
                     self.box.transform = CGAffineTransformIdentity;
                     self.box.frame = newFrame;
                     // </new code>
                     [UIView animateWithDuration:2.0
                                           delay:1.0
                          usingSpringWithDamping:1.0
                           initialSpringVelocity:0.0
                                         options:0
                                      animations:^{
                                        self.box.transform = [self _transformForSize:100.0 centerY:kEndCenterY];
                                      }
                                      completion:^(BOOL finished) {
                                      }];
                   }];

Используя тот же вызов -_transformForSize:centerY: в результате во второй анимации выполняется тот же Y-перевод, поэтому поле заканчивается на виде ниже, чем вы хотите, когда все сказано и сделано.

Чтобы это исправить, вам нужно вычислить deltaY на основе начальной координаты Y блока в конце первой анимации, а не исходной постоянной координаты Y:

- (CGAffineTransform)_transformForSize:(CGFloat)newSize centerY:(CGFloat)newCenterY
{
  CGFloat newScale = newSize / kStartSize;
  // Replace this line:
  CGFloat startCenterY = kStartY + kStartSize / 2.0;
  // With this one:
  CGFloat startCenterY = self.box.frame.origin.y + self.box.frame.size.height / 2.0;
  // </replace>
  CGFloat deltaY = newCenterY - startCenterY;
  CGAffineTransform translation = CGAffineTransformMakeTranslation(0.0, deltaY);
  CGAffineTransform scaling = CGAffineTransformMakeScale(newScale, newScale);
  return CGAffineTransformConcat(scaling, translation);
}

и это должно сработать.

ОБНОВИТЬ

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

Другие вопросы по тегам