执行多个连续 UIView 动画的最佳方法?

发布于 2024-09-25 13:42:16 字数 3464 浏览 2 评论 0原文

我有一系列 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

九命猫 2024-10-02 13:42:16

如果您愿意升级到 iOS 4.0,新的基于块的动画方法可以使动画链接变得微不足道。例如:

[UIView animateWithDuration:1.0 animations:^{ view.position = CGPointMake(0.0f, 0.0f); } completion:^(BOOL finished){
    [UIView animateWithDuration:0.2 animations:^{ view.alpha = 0.0; } completion:^(BOOL finished){
        [view removeFromSuperview]; }];
}]

将导致 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:

[UIView animateWithDuration:1.0 animations:^{ view.position = CGPointMake(0.0f, 0.0f); } completion:^(BOOL finished){
    [UIView animateWithDuration:0.2 animations:^{ view.alpha = 0.0; } completion:^(BOOL finished){
        [view removeFromSuperview]; }];
}]

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.

相守太难 2024-10-02 13:42:16

您可以使用块来实现此目的并获得非常干净的结果。

NSMutableArray* animationBlocks = [NSMutableArray new];

typedef void(^animationBlock)(BOOL);

// getNextAnimation
// removes the first block in the queue and returns it
animationBlock (^getNextAnimation)() = ^{
    animationBlock block = (animationBlock)[animationBlocks firstObject];
    if (block){
        [animationBlocks removeObjectAtIndex:0];
        return block;
    }else{
         return ^(BOOL finished){};
    }
};

//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
    [UIView animateWithDuration:1.0 animations:^{
        //...animation code...
    } completion: getNextAnimation()];
}];

//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
    [UIView animateWithDuration:1.0 animations:^{
        //...animation code...
    } completion: getNextAnimation()];
}];

//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
    NSLog(@"Multi-step Animation Complete!");
}];

// execute the first block in the queue
getNextAnimation()(YES);

摘自:http://xibxor.com/objective-c/uiview-animation-without-嵌套地狱/

You can use blocks for this purpose and get a very clean result.

NSMutableArray* animationBlocks = [NSMutableArray new];

typedef void(^animationBlock)(BOOL);

// getNextAnimation
// removes the first block in the queue and returns it
animationBlock (^getNextAnimation)() = ^{
    animationBlock block = (animationBlock)[animationBlocks firstObject];
    if (block){
        [animationBlocks removeObjectAtIndex:0];
        return block;
    }else{
         return ^(BOOL finished){};
    }
};

//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
    [UIView animateWithDuration:1.0 animations:^{
        //...animation code...
    } completion: getNextAnimation()];
}];

//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
    [UIView animateWithDuration:1.0 animations:^{
        //...animation code...
    } completion: getNextAnimation()];
}];

//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
    NSLog(@"Multi-step Animation Complete!");
}];

// execute the first block in the queue
getNextAnimation()(YES);

Taken from: http://xibxor.com/objective-c/uiview-animation-without-nested-hell/

凉宸 2024-10-02 13:42:16

我们创建了一个组件,用于使用块以声明方式链接动画步骤(Github 上的 CPAnimationSequence)。我们在我们的 iOS 开发博客中描述了动机和基本原理。

它为您提供了非常可读的代码,如下所示:

[[CPAnimationSequence sequenceWithSteps:
    [CPAnimationStep           for:0.25 animate:^{ self.imageView.alpha = 0.0; }],
    [CPAnimationStep           for:0.25 animate:^{ self.headline.alpha = 0.0;  }],
    [CPAnimationStep           for:0.25 animate:^{ self.content.alpha = 0.0;   }],
    [CPAnimationStep after:1.0 for:0.25 animate:^{ self.headline.alpha = 1.0;  }],
    [CPAnimationStep           for:0.25 animate:^{ self.content.alpha = 1.0;   }],
    nil]
runAnimated:YES];

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:

[[CPAnimationSequence sequenceWithSteps:
    [CPAnimationStep           for:0.25 animate:^{ self.imageView.alpha = 0.0; }],
    [CPAnimationStep           for:0.25 animate:^{ self.headline.alpha = 0.0;  }],
    [CPAnimationStep           for:0.25 animate:^{ self.content.alpha = 0.0;   }],
    [CPAnimationStep after:1.0 for:0.25 animate:^{ self.headline.alpha = 1.0;  }],
    [CPAnimationStep           for:0.25 animate:^{ self.content.alpha = 1.0;   }],
    nil]
runAnimated:YES];
拿命拼未来 2024-10-02 13:42:16

“上下文”是 void*,因此不保留。有几个选项:

  • 设置时保留它。在回调开始时自动释放它。这感觉很恶心(Objective-C 函数应该适当地保留/释放,但不幸的是上下文不是id)。
  • 将数字存储在字符串中:intanimationStep = [animationID intValue];[UIView beginAnimations:[NSString stringWithFormat:@"%d", nextAnimationStep] context:NULL];。这也让人感觉恶心。
  • 假设 UIKit/CoreAnimation 不取消引用指针,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:

  • Retain it when you set it. Autorelease it at the start of the callback. This feels icky (Objective-C functions ought to retain/release appropriately, but unfortunately the context isn't an id).
  • Store the number in the string: int animationStep = [animationID intValue]; and [UIView beginAnimations:[NSString stringWithFormat:@"%d", nextAnimationStep] context:NULL];. This also feels icky.
  • Assuming that UIKit/CoreAnimation doesn't dereference the pointer, 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 be finished:(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).

雨巷深深 2024-10-02 13:42:16
 // create the view that will execute our animation
 UIImageView* logo = [[UIImageView alloc] initWithFrame:self.view.frame];
 // load all the frames of our animation
 logo.animationImages = [NSArray arrayWithObjects:  
                                 [UIImage imageNamed:@"frame_00001.png"],

                         [UIImage imageNamed:@"frame_00002.png"],
                         [UIImage imageNamed:@"frame_00003.png"],
                         [UIImage imageNamed:@"frame_00004.png"],
                                 [UIImage imageNamed:@"frame_00005"], nil];

 // all frames will execute in 3 seconds
 logo.animationDuration =3.3;
 // repeat the annimation forever
logo.animationRepeatCount = 1;
 // start animating
 [logo startAnimating];
 // add the animation view to the main window 
 [self.view addSubview:logo];
 [logo release]; 

将其放入 ViewDidLoad 中

 // create the view that will execute our animation
 UIImageView* logo = [[UIImageView alloc] initWithFrame:self.view.frame];
 // load all the frames of our animation
 logo.animationImages = [NSArray arrayWithObjects:  
                                 [UIImage imageNamed:@"frame_00001.png"],

                         [UIImage imageNamed:@"frame_00002.png"],
                         [UIImage imageNamed:@"frame_00003.png"],
                         [UIImage imageNamed:@"frame_00004.png"],
                                 [UIImage imageNamed:@"frame_00005"], nil];

 // all frames will execute in 3 seconds
 logo.animationDuration =3.3;
 // repeat the annimation forever
logo.animationRepeatCount = 1;
 // start animating
 [logo startAnimating];
 // add the animation view to the main window 
 [self.view addSubview:logo];
 [logo release]; 

put this in the ViewDidLoad

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文