【问题标题】:stopping UIImageView animation gradually and smoothly逐渐平滑地停止 UIImageView 动画
【发布时间】:2025-12-08 21:10:02
【问题描述】:

我有以下简单的 UIImageView 动画:

-(void) setupTheAnimation {
  self.imgView.animationImages = imagesArr;
  [self.imgView setAnimationRepeatCount:-1];
  self.imgView.animationDuration =0.9;
  [self.imgView startAnimating];
  [self performSelector:@selector(stopTheAnimation) withObject:nil afterDelay:4.0];
}

-(void) stopTheAnimation {
  [self.imgView stopAnimating];
}

但是当动画停止时我遇到了一个问题我不知道它停止的最后一帧是什么!所以动画的结局一点都不流畅。

所以我需要:

1)知道动画结束的最后一帧是什么,所以我将它设置为动画的最后一个图像,这会导致平滑停止。

2)逐渐停止此动画,即在停止之前更改其持续时间以先减慢然后停止。

This is a link to the sample project.

【问题讨论】:

  • 我想说这需要在UIImageView 的子类上实现为自定义动画属性,您可以在其中将图层的内容重复设置为传入的图像数组中的图像提供定时功能。不会太难,但我认为使用UIImageViewanimationImages 实现缓和效果是不可能的。

标签: iphone ios objective-c uiimageview


【解决方案1】:

我知道您的原始实现使用animationImages,但我不知道有一种方法可以使用animationImages 直接 持续可变的持续时间。但是,这是一个非常简单的功能,可以自己实现。如果这样做,那么您可以在数组中的图像之间编写动态持续时间值。

在以下代码中,我将animationImages 替换为自定义步进函数,并动态调整请求停止后 的持续时间。请注意,这与指定硬结束时间的原始代码略有不同。此代码指定齿轮旋转应开始减速的时间。

如果你真的有一个硬动画周期,你可以调整对stopTheAnimation的调用来考虑你选择的减速因素(我只是在减速期间每步增加10%的持续时间,直到步数比给定的阈值):

// my animation stops when the step duration reaches this value:
#define STOP_THRESHOLD_SECONDS 0.1f
#define NUM_IMAGES 35

@implementation ViewController
{
    NSMutableArray *imagesArr;
    int currentImage;
    BOOL stopRequested;
    NSTimeInterval duration;
}

-(void) setupTheAnimation {

    stopRequested = NO;
    currentImage = 0;
    duration = 0.9f / NUM_IMAGES;
    [self stepThroughImages];

    [self performSelector:@selector(stopTheAnimation) withObject:nil afterDelay:4.0];
}

- (void) stepThroughImages {

    self.imgView.image = [imagesArr objectAtIndex: currentImage];

    if (currentImage == NUM_IMAGES - 1) {
        currentImage = 0;
    } else {
        currentImage++;
    }

    if (stopRequested && duration < STOP_THRESHOLD_SECONDS) {
        // we're slowing down gradually
        duration *= 1.1f;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            [self stepThroughImages];
        });
    } else if (!stopRequested) {
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            [self stepThroughImages];
        });
    }
}

-(void) stopTheAnimation {
    stopRequested = YES;
}

【讨论】:

  • 不客气。我刚刚注意到您的项目中实际上有 36 个图像(0 到 35)。所以,也许你应该将我的代码中的NUM_IMAGES 更改为36,因为我从0 迭代到NUM_IMAGES - 1。我认为在您的 viewDidLoad 方法中初始化 imagesArr 的代码也有同样的问题。它实际上并没有加载GearAnimation35@2x.png
【解决方案2】:

我只通过使用块知道这一点,但你可以这样做:

    [UIImageView animateWithDuration:0.9f delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{

                // animate what you want
                self.imgView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);

            } completion:^(BOOL finished){

                // animation is completed
                self.imgView.image = image;

            }];

“UIViewAnimationOptionCurveEaseOut”选项应该会逐渐停止您的动画。您可以尝试使用其他选项来亲自查看效果。

【讨论】:

  • 这不适用于我在问题中发布的 animationImages 方法。
【解决方案3】:

您的方法有一个问题:每秒的帧数不是恒定的(您减慢了时间)。

您确定需要帧动画吗?

如果您的动画可以通过几何(仿射)变换实现,那就更好了。

如果您的动画可以通过以下组合完成:

  • 翻译
  • 旋转
  • 缩放

那么这是一个仿射变换。

【讨论】:

  • 如果他们是从头开始完成这项任务,我会 100% 同意。 3 个齿轮中的每一个都可以是使用仿射变换旋转的单独图像。但是,由于他们已经获得了 36 张图片,我想说这可能是一项不错的工作(并且可能超出了 编程 的范围,并进入了 图形设计)。必须提取四个或五个单独的图像:背景、2/3 齿轮和放置在顶部的边框。此外,我认为齿轮运动的棘轮特性也使得基于帧的动画的轻微波动在这里可以接受。
  • 谢谢内特。我不知道这 36 幅齿轮图像,因为原始问题中没有提到这一点。但是你的方法是可以接受的,但最终的渲染质量仍然很差,因为时间对于给定的速度是离散的,你需要放慢这个速度。无论如何。
  • 是的,您必须实际下载完整的项目才能看到这一点,因此仅从问题来看肯定不是很明显:)