Record Your Core Animation Animation(video)

本文介绍如何结合使用Core Animation和QuickTime技术,将运行中的Core Animation动画捕获并保存为QuickTime电影文件。通过创建多个关键帧动画并设置定时器抓取每一帧画面,最终实现动画的录制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Record Your Core Animation Animation

by Matt Long

Every once in a while I find a way to combine multiple technologies that, while they don’t produce anything terribly useful, are very interesting when combined. In this post I will be taking a look at combining Core Animation and QuickTime. As you may or may not be aware, you can draw in a graphics context while your Core Animation animation is running and add each image created to a QTMovie object from QTKit. This enables you to create a QuickTime movie of your Core Animation animation. Here’s how.

The basic process flow goes like this. Clicking the ‘Capture’ button on the user interface calls an IBAction called-saveAnimation. It prompts the user to select an output file for the movie. When the user has selected the file, the animations are created and added to the layer. Next we create a timer that is going to call a function that will grab the current frame and place it into the QTMovie object using -addImage at a specified interval. We set our AppDelegate to also be the delegate for the animation group so that when the animation completes we get notified and can then write our QTMovie object data to disk.

Use An Interesting Animation

First you are going to need an animation that is worth recording. Of course any old animation will do, but we’ll keep it interesting by adding multiple animations to a single layer. I have created four different keyframe animations that we will add to an animation group. The keypaths are “backgroundColor”, “borderWidth”, “position”, and “bounds”. Check the sample code to see how these animations are constructed.

We set the duration for all of the animations to five seconds. We also need to make sure that we set the duration for the group itself, otherwise it will override the five second duration we set for the animations themselves and run in the default 0.25 seconds. The code below shows how the animations are added to the layer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)loadAnimations;
{
  CAAnimationGroup *group = [CAAnimationGroup animation];
 
  [group setAnimations:[NSArray arrayWithObjects:[self backgroundColorAnimation],
                          [self borderWidthAnimation],
                          [self positionAnimation],
                          [self boundsAnimation], nil]];
 
  [group setValue:@"mainGroup" forKey:@"name"];
  [group setDuration:5.0];
  [group setAutoreverses:YES];
  [group setDelegate:self];
 
  [layer addAnimation:group forKey:@"group"];  
}

Notice that we have used KVC here to set a name for the animation group. We will use this as a tag later to make sure the animation that triggers our-animationDidStop:finished animation delegate is the correct one. More on that later.

Get Our Movie Ready

Prior to loading the animations, we prompt the user to select a file to write the movie file to. After loading the animations, we start our timer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (IBAction)saveAnimation:(id)sender;
{
  NSSavePanel *savePanel;
 
  savePanel = [NSSavePanel savePanel];
  [savePanel setExtensionHidden:YES];
  [savePanel setCanSelectHiddenExtension:NO];
  [savePanel setTreatsFilePackagesAsDirectories:NO];
 
  if( [savePanel runModal] == NSOKButton )
  {
    movie = [[QTMovie alloc] initToWritableFile:[savePanel filename] error:nil];    
  }
 
  [self loadAnimations];
 
  timer = [NSTimer scheduledTimerWithTimeInterval:1.0/(NSTimeInterval)10.0
                                           target:self
                                         selector:@selector(updateTime:)
                                         userInfo:NULL
                                          repeats:YES];    
 
}

Our -updateTime selector will get called every 1/10th of a second and will grab the current frame to save it to the QTMoive object. Here is the-updateTime code.

1
2
3
4
5
6
7
8
9
10
11
- (void)updateTime:(NSTimer*)theTimer;
{
  NSBitmapImageRep *image = [self getCurrentFrame];
 
  QTTime time = QTMakeTime(1, 10);
  NSDictionary *attrs = [NSDictionary dictionaryWithObject:@"png " forKey:QTAddImageCodecType];
  NSImage *img = [[NSImage alloc] initWithData:[image TIFFRepresentation]];
  [movie addImage:img forDuration:time withAttributes:attrs];
 
  [image release];
}

Obtaining the Current Frame

The code to obtain the current frame is somewhat lengthy, but the concepts are pretty simple. We need to create a graphics context that we can draw into and then draw into it using the presentationLayer of the contenView’s root layer. If you’re not familiar, the presentationLayer provides the current state of the animated fields while “in-flight”.

Core Animation doesn’t provide any callbacks for when a frame is ready to be displayed which is why we are using a timer. This means that we may be capturing more frames than we need to, so getting the right frame rate takes a bit of trial and error, which I have to confess I wasn’t able to get nailed down completely. I’m still working on it and will update here when I get that part figured out. Meanwhile, here is the code for-getCurrentFrame

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
- (NSBitmapImageRep*)getCurrentFrame;
{
  CGContextRef    context = NULL;
  CGColorSpaceRef colorSpace;
  int bitmapByteCount;
  int bitmapBytesPerRow;
 
  int pixelsHigh = (int)[[[window contentView] layer] bounds].size.height;
  int pixelsWide = (int)[[[window contentView] layer] bounds].size.width;
 
  bitmapBytesPerRow   = (pixelsWide * 4);
  bitmapByteCount     = (bitmapBytesPerRow * pixelsHigh);
 
  colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
 
  context = CGBitmapContextCreate (NULL,
                                   pixelsWide,
                                   pixelsHigh,
                                   8,
                                   bitmapBytesPerRow,
                                   colorSpace,
                                   kCGImageAlphaPremultipliedLast);
  if (context== NULL)
  {
    NSLog(@"Failed to create context.");
    return nil;
  }
 
  CGColorSpaceRelease( colorSpace );
 
  [[[[window contentView] layer] presentationLayer] renderInContext:context];
 
  CGImageRef img = CGBitmapContextCreateImage(context);
  NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithCGImage:img];
  CFRelease(img);
 
  return bitmap;
 
}

Notice that we are calling -renderInContext on the presentationLayer of the window’s contentView’s root layer. If we were only to render our animated layer, we wouldn’t be able to see the animation as it will only render the containing rectangle of the animating layer.

Finishing Up

Finally we need to write the movie data out to disk. The QTMovie object provides a single call to do so, but we need a way to know when the animation has finished so we can make this call. When we created our animation group, we set its delegate to our AppDelegate which will cause the delegate method -animationDidStop:finished to get called. Remember that setting the delegate for each of the individual animations gets ignored when you are using an animation group. We implement the-animationDidStop:finished delegate as shown below.

1
2
3
4
5
6
7
8
9
10
11
12
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag;
{
  NSLog(@"Animation stopped: %@", theAnimation);
 
  id name = [theAnimation valueForKey:@"name"];
  if( name )
    if( [name isEqualToString:@"mainGroup"] )
    {
      [movie updateMovieFile];
      [timer invalidate];
    }
}

The first thing we do is check the animation tag we set when creating the animation group. This really isn’t necessary in this code example since there is only one animation that is going to use it, but this code shows you how to differentiate if you were to use multiple animations or groups and wanted to know when each of them finished animating.

The call to -updateMovieFile writes the data to disk and we now have a QuickTime movie that will play our animation. Open the resulting file in QuickTime or just invoke QuickLook to see the result.

Conclusion

Maybe you can think of a use for this kind of thing. I haven’t yet–other than for writing a blog post of course. Shoot me your thoughts and comments in the comments section. Until next time.

CA Animation Capture Demo Project


http://www.cimgf.com/2009/02/03/record-your-core-animation-animation/

标题基于SpringBoot+Vue的学生交流互助平台研究AI更换标题第1章引言介绍学生交流互助平台的研究背景、意义、现状、方法与创新点。1.1研究背景与意义分析学生交流互助平台在当前教育环境下的需求及其重要性。1.2国内外研究现状综述国内外在学生交流互助平台方面的研究进展与实践应用。1.3研究方法与创新点概述本研究采用的方法论、技术路线及预期的创新成果。第2章相关理论阐述SpringBoot与Vue框架的理论基础及在学生交流互助平台中的应用。2.1SpringBoot框架概述介绍SpringBoot框架的核心思想、特点及优势。2.2Vue框架概述阐述Vue框架的基本原理、组件化开发思想及与前端的交互机制。2.3SpringBoot与Vue的整合应用探讨SpringBoot与Vue在学生交流互助平台中的整合方式及优势。第3章平台需求分析深入分析学生交流互助平台的功能需求、非功能需求及用户体验要求。3.1功能需求分析详细阐述平台的各项功能需求,如用户管理、信息交流、互助学习等。3.2非功能需求分析对平台的性能、安全性、可扩展性等非功能需求进行分析。3.3用户体验要求从用户角度出发,提出平台在易用性、美观性等方面的要求。第4章平台设计与实现具体描述学生交流互助平台的架构设计、功能实现及前后端交互细节。4.1平台架构设计给出平台的整体架构设计,包括前后端分离、微服务架构等思想的应用。4.2功能模块实现详细阐述各个功能模块的实现过程,如用户登录注册、信息发布与查看、在线交流等。4.3前后端交互细节介绍前后端数据交互的方式、接口设计及数据传输过程中的安全问题。第5章平台测试与优化对平台进行全面的测试,发现并解决潜在问题,同时进行优化以提高性能。5.1测试环境与方案介绍测试环境的搭建及所采用的测试方案,包括单元测试、集成测试等。5.2测试结果分析对测试结果进行详细分析,找出问题的根源并
内容概要:本文详细介绍了一个基于灰狼优化算法(GWO)优化的卷积双向长短期记忆神经网络(CNN-BiLSTM)融合注意力机制的多变量多步时间序列预测项目。该项目旨在解决传统时序预测方法难以捕捉非线性、复杂时序依赖关系的问题,通过融合CNN的空间特征提取、BiLSTM的时序建模能力及注意力机制的动态权重调节能力,实现对多变量多步时间序列的精准预测。项目不仅涵盖了数据预处理、模型构建与训练、性能评估,还包括了GUI界面的设计与实现。此外,文章还讨论了模型的部署、应用领域及其未来改进方向。 适合人群:具备一定编程基础,特别是对深度学习、时间序列预测及优化算法有一定了解的研发人员和数据科学家。 使用场景及目标:①用于智能电网负荷预测、金融市场多资产价格预测、环境气象多参数预报、智能制造设备状态监测与预测维护、交通流量预测与智慧交通管理、医疗健康多指标预测等领域;②提升多变量多步时间序列预测精度,优化资源调度和风险管控;③实现自动化超参数优化,降低人工调参成本,提高模型训练效率;④增强模型对复杂时序数据特征的学习能力,促进智能决策支持应用。 阅读建议:此资源不仅提供了详细的代码实现和模型架构解析,还深入探讨了模型优化和实际应用中的挑战与解决方案。因此,在学习过程中,建议结合理论与实践,逐步理解各个模块的功能和实现细节,并尝试在自己的项目中应用这些技术和方法。同时,注意数据预处理的重要性,合理设置模型参数与网络结构,控制多步预测误差传播,防范过拟合,规划计算资源与训练时间,关注模型的可解释性和透明度,以及持续更新与迭代模型,以适应数据分布的变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值