【问题标题】:Animation iOS with sound file带有声音文件的动画 iOS
【发布时间】:2025-12-16 11:20:03
【问题描述】:

我有一些动画需要在非常特定的时间出现在屏幕上,这些动画存储在 SQLite 数据库中。我打算做的是使用 nstimer 来保持时间并在达到特定时间时弹出动画。

我正在考虑使用 NSTimer 来计算声音文件和动画的持续时间,然后在达到某些点时弹出屏幕动画。问题是设置的时间是这样的 55.715000 秒,所以非常准确,这些需要与将与动画一起播放的音轨同步。

首先这是否可能,其次我如何比较这些具体的时间,我似乎面临的问题是代码运行速度不够快,时间跳跃超过 0.001 秒。

我不了解 openGLES 或 Cocos2d,学习这些在时间尺度上是不可行的。

【问题讨论】:

  • 除非超过 0.25 秒,否则您的用户不会注意到动画与音频不同步。如果你能把事情精确到 0.1 秒,那你就没事了。
  • 这个想法是每秒发射大约 4 个项目,一些重复项目让我担心一个会遇到另一个,最终会出现一个而不是 2 个。我的另一个担心是旧硬件,这可能会导致更大的滞后。
  • 我最近做了一些类似的事情,我所做的是让动画完成例程停止声音(如果有指示,则开始另一个)。如果缺少十分之一秒的声音,大多数人都不会注意到。

标签: objective-c ios animation uiviewcontroller


【解决方案1】:

如果您的视觉效果需要与音频完全同步(例如,动画必须出现在节拍上的音乐应用程序),那么您需要使用以下方法。它适用于非常旧的 iPhone 硬件,在运行时基本上不会出错。

  1. 预渲染视觉效果的每个“帧”,以便将每个“帧”存储为全屏图像。这可以在桌面上完成,也可以在手机上运行渲染逻辑,然后将输出作为像素捕获到文件中。

  2. 将每一帧保存为像素数组后,通过 iOS 上的标准 AVAudioPlayer API 播放音频。此 API 负责音频播放并报告您将用来确定要显示哪个视频帧的时间。

  3. 播放音频后,获取“时间”并将其除以视频帧率,以确定要显示的 N 个图像数组中的哪个图像。获取图像数据并将其包装在 CGImageRef/UIImage 中,这将以最佳方式将图像数据传输到屏幕。

如果您想查看此方法的有效源代码,请查看AVSync。此示例代码显示了我在应用商店中名为 iPractice 的应用中使用的实现。它非常快,即使在旧 iPhone 3G 上也能以 30FPS 运行。

【讨论】:

    【解决方案2】:

    无论您的计时有多准确,设备的显示频率都不会超过 60Hz(每帧 16.7 毫秒)。在我看来,您至少有两个选择:

    1) 使用CADisplayLink 回调来检查音频的播放进度,并在每个动画及时触发。

    显示链接计时器的创建方式与常规 NSTimers 类似:

    -(void)viewDidLoad
    {
        // ...
        self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(scheduleAnimations:)];
        [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    }
    
    - (void)scheduleAnimations:(CADisplayLink *)displayLink
    {
        // get playback time from player object
        // iterate queue of timings
        //     if the timestamp of a timing is equal to or less than the playback time
        //         create its animation for immediate execution
        //         remove it from the queue
    }
    

    2) 创建动画集合,将它们各自的beginTime 属性设置为适当的触发时间(或者,如果使用隐式或基于块的动画,则使用delay 参数)。

    [CATransaction begin];
    // iterate collection of timings
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"key"];
        animation.startTime = /* time to trigger animation */
        animation.removedOnCompletion = NO;
        [layer addAnimation:animation forKey:nil];
    [CATransaction commit];
    

    【讨论】:

    • 这听起来可能是我可以看到的问题是在 3 分钟的声音文件中有大约一百个动画要做。所以第一个选项可能是最好的我主要关心的问题是落后于旧硬件。
    • 是的,如果你有多个动画要执行,肯定会使用前一种方法。