如何在单独的动画中将 CGAffineTransform 链接在一起?
How to chain CGAffineTransform together in separate animations?
我需要一个 UIView 上的两个动画:
- 使视图向下移动并略微增大。
- 使视图围绕其新中心变得更大。
当我尝试这样做时,第二个动画在一个奇怪的位置开始,但在正确的位置和大小结束。如何让第二个动画从第一个动画结束的位置开始?
#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 时,它无法顺利缩放。
这看起来 可能 是一个 UIKit 错误,涉及当您在现有视图之上应用转换时 UIView 如何解析其布局。通过在第二个完成块的最开始执行以下操作,我至少能够获得第二个动画的起始坐标:
[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 平移,但是,当一切都说完之后,框最终在视图中比您想要的更靠下。
要解决这个问题,您需要根据盒子在第一个动画的 end 处的起始 Y 坐标而不是原始的常量 Y 坐标来计算 deltaY:
- (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);
}
它应该可以解决问题。
更新
我应该说,我认为这 "trick" 更像是一种解决方案,而不是实际解决方案,但是盒子的框架和变换在动画管道的每个阶段看起来都是正确的,所以如果有一个真正的 "solution" 此刻它在躲避我。这个技巧至少为你解决了翻译问题,所以你可以尝试更复杂的视图层次结构,看看你能走多远。
我需要一个 UIView 上的两个动画:
- 使视图向下移动并略微增大。
- 使视图围绕其新中心变得更大。
当我尝试这样做时,第二个动画在一个奇怪的位置开始,但在正确的位置和大小结束。如何让第二个动画从第一个动画结束的位置开始?
#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 时,它无法顺利缩放。
这看起来 可能 是一个 UIKit 错误,涉及当您在现有视图之上应用转换时 UIView 如何解析其布局。通过在第二个完成块的最开始执行以下操作,我至少能够获得第二个动画的起始坐标:
[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 平移,但是,当一切都说完之后,框最终在视图中比您想要的更靠下。
要解决这个问题,您需要根据盒子在第一个动画的 end 处的起始 Y 坐标而不是原始的常量 Y 坐标来计算 deltaY:
- (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);
}
它应该可以解决问题。
更新
我应该说,我认为这 "trick" 更像是一种解决方案,而不是实际解决方案,但是盒子的框架和变换在动画管道的每个阶段看起来都是正确的,所以如果有一个真正的 "solution" 此刻它在躲避我。这个技巧至少为你解决了翻译问题,所以你可以尝试更复杂的视图层次结构,看看你能走多远。