执行多个连续 UIView 动画的最佳方法?
我有一系列 8 个 UIView 动画,这些动画在我的视图加载后立即发生。现在,我通过使用animationDidStop:finished:context 委托方法来完成此任务,一切都按预期进行。问题是我对每个动画都有一个新方法。这些方法中的大部分代码都是重复的,只有动画持续时间和元素的实际位置发生变化。
我尝试创建一个将被调用的方法,并让上下文保存适当更改 UI 所需的参数,但它似乎递归地调用自身,远远超过了我调用它的次数:
-(void)animationDidStop:(NSString *)animationID finished:(BOOL)finished context:(void *)context{
NSNumber *number = (NSNumber *)context;
int animationStep = [number intValue];
int nextAnimationStep = animationStep + 1;
NSNumber *nextAnimationStepNumber = [NSNumber numberWithInt:nextAnimationStep];
NSLog(@"Animation Step: %i", animationStep);
CGRect firstFrame = CGRectMake(self.feedsScroll.frame.size.width * 2, 0.0f, self.secondFeedView.view.frame.size.width, self.secondFeedView.view.frame.size.height);
CGRect thirdFrame = CGRectMake(self.feedsScroll.frame.size.width * 2, 0.0f, self.thirdFeedView.view.frame.size.width, self.thirdFeedView.view.frame.size.height);
[UIView beginAnimations:nil context:nextAnimationStepNumber];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDelegate:self];
if (animationStep < 8)
[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
NSLog(@"Beginning animations");
switch (animationStep) {
case 0:
[UIView setAnimationDuration:.3];
self.firstFeedView.view.center = CGPointMake(self.firstFeedView.view.center.x + 30, self.firstFeedView.view.center.y);
break;
case 1:
[UIView setAnimationDuration:.3];
self.firstFeedView.view.center = CGPointMake(self.firstFeedView.view.center.x - 30, self.firstFeedView.view.center.y);
break;
case 2:
[UIView setAnimationDuration:.3];
[self.secondFeedView.view setFrame:firstFrame];
break;
case 3:
[UIView setAnimationDuration:.3];
self.secondFeedView.view.center = CGPointMake(self.secondFeedView.view.center.x + 30, self.firstFeedView.view.center.y);
break;
case 4:
[UIView setAnimationDuration:.3];
self.secondFeedView.view.center = CGPointMake(self.secondFeedView.view.center.x - 30, self.firstFeedView.view.center.y);
break;
case 5:
NSLog(@"Animation step 6");
[UIView setAnimationDuration:.5];
self.firstFeedView.view.center = CGPointMake(self.firstFeedView.view.center.x - 230, self.firstFeedView.view.center.y);
self.secondFeedView.view.center = CGPointMake(self.secondFeedView.view.center.x - 230, self.firstFeedView.view.center.y);
break;
case 6:
[UIView setAnimationDuration:.5];
[self.thirdFeedView.view setFrame:thirdFrame];
break;
case 7:
[UIView setAnimationDuration:.3];
self.thirdFeedView.view.center = CGPointMake(self.thirdFeedView.view.center.x + 30, self.firstFeedView.view.center.y);
break;
case 8:
[UIView setAnimationDuration:.3];
self.thirdFeedView.view.center = CGPointMake(self.thirdFeedView.view.center.x - 30, self.thirdFeedView.view.center.y);
break;
default:
break;
}
[UIView commitAnimations];
}
我知道这可能是一个天真的想法执行。我是 iPhone 开发新手,正在寻找一些可以在这里应用的最佳实践。我是否以错误的方式处理这件事?
I have a series of 8 UIView animations that occur directly after my view is loaded. Right now, I am accomplishing this by using the animationDidStop:finished:context
delegate method, and everything works as expected. The problem is that I have a new method for each animation. Most of the code in each of these methods is repeated, with only the animation duration and the actual positioning of elements changing.
I tried to create a single method which would be called and have the context hold the parameters needed to change the UI appropriately, but it seems to be recursively calling itself well past the amount of times I call it:
-(void)animationDidStop:(NSString *)animationID finished:(BOOL)finished context:(void *)context{
NSNumber *number = (NSNumber *)context;
int animationStep = [number intValue];
int nextAnimationStep = animationStep + 1;
NSNumber *nextAnimationStepNumber = [NSNumber numberWithInt:nextAnimationStep];
NSLog(@"Animation Step: %i", animationStep);
CGRect firstFrame = CGRectMake(self.feedsScroll.frame.size.width * 2, 0.0f, self.secondFeedView.view.frame.size.width, self.secondFeedView.view.frame.size.height);
CGRect thirdFrame = CGRectMake(self.feedsScroll.frame.size.width * 2, 0.0f, self.thirdFeedView.view.frame.size.width, self.thirdFeedView.view.frame.size.height);
[UIView beginAnimations:nil context:nextAnimationStepNumber];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDelegate:self];
if (animationStep < 8)
[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
NSLog(@"Beginning animations");
switch (animationStep) {
case 0:
[UIView setAnimationDuration:.3];
self.firstFeedView.view.center = CGPointMake(self.firstFeedView.view.center.x + 30, self.firstFeedView.view.center.y);
break;
case 1:
[UIView setAnimationDuration:.3];
self.firstFeedView.view.center = CGPointMake(self.firstFeedView.view.center.x - 30, self.firstFeedView.view.center.y);
break;
case 2:
[UIView setAnimationDuration:.3];
[self.secondFeedView.view setFrame:firstFrame];
break;
case 3:
[UIView setAnimationDuration:.3];
self.secondFeedView.view.center = CGPointMake(self.secondFeedView.view.center.x + 30, self.firstFeedView.view.center.y);
break;
case 4:
[UIView setAnimationDuration:.3];
self.secondFeedView.view.center = CGPointMake(self.secondFeedView.view.center.x - 30, self.firstFeedView.view.center.y);
break;
case 5:
NSLog(@"Animation step 6");
[UIView setAnimationDuration:.5];
self.firstFeedView.view.center = CGPointMake(self.firstFeedView.view.center.x - 230, self.firstFeedView.view.center.y);
self.secondFeedView.view.center = CGPointMake(self.secondFeedView.view.center.x - 230, self.firstFeedView.view.center.y);
break;
case 6:
[UIView setAnimationDuration:.5];
[self.thirdFeedView.view setFrame:thirdFrame];
break;
case 7:
[UIView setAnimationDuration:.3];
self.thirdFeedView.view.center = CGPointMake(self.thirdFeedView.view.center.x + 30, self.firstFeedView.view.center.y);
break;
case 8:
[UIView setAnimationDuration:.3];
self.thirdFeedView.view.center = CGPointMake(self.thirdFeedView.view.center.x - 30, self.thirdFeedView.view.center.y);
break;
default:
break;
}
[UIView commitAnimations];
}
I know this is probably a naive implementation. I am new to iPhone development, and am looking for some best practices to apply here. Am I going about this in the wrong way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
如果您愿意升级到 iOS 4.0,新的基于块的动画方法可以使动画链接变得微不足道。例如:
将导致
view
在 1 秒的时间内动画到 (0, 0),淡出 0.2 秒,然后从其超级视图中删除。这些动画将是连续的。+animateWithDuration:animations:completion:
类方法中的第一个块包含立即进行动画处理的属性更改,第二个块是动画完成时要执行的操作。由于此回调可以包含此类的另一个动画,因此您可以嵌套它们以创建任意动画链。If you're willing to step up to iOS 4.0, the new blocks-based animation approach can make this animation chaining trivial. For example:
will cause
view
to animate to (0, 0) over a duration of 1 second, fade out for 0.2 seconds, then be removed from its superview. These animations will be sequential.The first block in the
+animateWithDuration:animations:completion:
class method contains the property changes to animate at once, and the second is the action to be performed on completion of the animation. Because this callback can contain another animation of this sort, you can nest them to create arbitrary chains of animations.您可以使用块来实现此目的并获得非常干净的结果。
摘自:http://xibxor.com/objective-c/uiview-animation-without-嵌套地狱/
You can use blocks for this purpose and get a very clean result.
Taken from: http://xibxor.com/objective-c/uiview-animation-without-nested-hell/
我们创建了一个组件,用于使用块以声明方式链接动画步骤(Github 上的 CPAnimationSequence)。我们在我们的 iOS 开发博客中描述了动机和基本原理。
它为您提供了非常可读的代码,如下所示:
We created a component for chaining animation steps declaratively using blocks (CPAnimationSequence on Github). We described the motivations and the rationale in our iOS development blog.
It gives you very readable code, something like this:
“上下文”是 void*,因此不保留。有几个选项:
id
)。intanimationStep = [animationID intValue];
和[UIView beginAnimations:[NSString stringWithFormat:@"%d", nextAnimationStep] context:NULL];
。这也让人感觉恶心。int AnimationStep = (int)context;
和[UIView beginAnimations:nil context:(void*)nextAnimationStep];
。这很糟糕(根据 C 标准,可能会导致未定义的行为)。另一方面,
finished:(BOOL)finished
应该是finished:(NSNumber*)finished
。他们在原始文档中犯了一个错误;更改文档以反映 API 可能更容易,而不是更改 API 并必须神奇地保持向后兼容性(即使传递 bool 更明智)。The "context" is a void*, and therefore not retained. There are a few options:
id
).int animationStep = [animationID intValue];
and[UIView beginAnimations:[NSString stringWithFormat:@"%d", nextAnimationStep] context:NULL];
. This also feels icky.int animationStep = (int)context;
and[UIView beginAnimations:nil context:(void*)nextAnimationStep];
. This is icky (and probably causes undefined behaviour according to the C standard).On another note,
finished:(BOOL)finished
should befinished:(NSNumber*)finished
. They made a mistake in the original docs; it was presumably easier to change the docs to reflect the API instead of changing the API and having to magically maintain backwards compatibility (even though passing a bool is more sensible).将其放入 ViewDidLoad 中
put this in the ViewDidLoad