iOS是否有CADisplayLink,在Mac OS X中有CVDisplayLink,但我找不到使用它的方法,所有示例都与OpenGL有关。
我创建了这个自定义UIView,我想把它翻译成NSView
#import "StarView.h"
#import <QuartzCore/QuartzCore.h>
#define MAX_FPS (100.0)
#define MIN_FPS (MAX_FPS/40.0)
#define FRAME_TIME (1.0 / MAX_FPS)
#define MAX_CPF (MAX_FPS / MIN_FPS)
#define aEPS (0.0001f)
@implementation StarView
@synthesize starImage = _starImage;
@synthesize x, y;
- (void)baseInit {
_starImage = nil;
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(runLoop)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self baseInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
[self baseInit];
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
[self.starImage drawAtPoint:CGPointMake(self.x, self.y)];
}
-(void) cycle {
self.x += 5;
if (self.x > 230) {
self.x = 0;
}
}
- (void) runLoop {
//NSLog(@"called");
static CFTimeInterval last_time = 0.0f;
static float cycles_left_over = 0.0f;
static float dt2 = 0.0f;
float dropAnimRate = (2.1f/25.0f);
CFTimeInterval current_time = CACurrentMediaTime();
float dt = current_time - last_time + cycles_left_over;
dt2 += dt;
[self setNeedsDisplay];
if (dt > (MAX_CPF * FRAME_TIME)) {
dt = (MAX_CPF * FRAME_TIME);
}
while (dt > FRAME_TIME) {
if (dt2 > (dropAnimRate - aEPS)){
[self cycle];
dt2 = 0.0f;
}
dt -= FRAME_TIME;
}
cycles_left_over = dt;
last_time = current_time;
}
@end
我无法翻译的部分是这一部分
- (void)baseInit {
_starImage = nil;
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(runLoop)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
我知道我可以使用NSTimer,但它没有相同的准确度
答案 0 :(得分:20)
您可以将CVDisplayLink配置为独立于OpenGL工作。以下是我用来设置CVDisplayLink以触发工业相机定期捕获和渲染的代码:
CGDirectDisplayID displayID = CGMainDisplayID();
CVReturn error = kCVReturnSuccess;
error = CVDisplayLinkCreateWithCGDisplay(displayID, &displayLink);
if (error)
{
NSLog(@"DisplayLink created with error:%d", error);
displayLink = NULL;
}
CVDisplayLinkSetOutputCallback(displayLink, renderCallback, (__bridge void *)self);
我的renderCallback
函数看起来像这样:
static CVReturn renderCallback(CVDisplayLinkRef displayLink,
const CVTimeStamp *inNow,
const CVTimeStamp *inOutputTime,
CVOptionFlags flagsIn,
CVOptionFlags *flagsOut,
void *displayLinkContext)
{
return [(__bridge SPVideoView *)displayLinkContext renderTime:inOutputTime];
}
当然,你必须用你自己的类和回调方法替换上面的内容。代码中的__bridge
用于ARC支持,因此您可能不需要在手动引用计数环境中。
要开始从显示链接捕获事件,请使用
CVDisplayLinkStart(displayLink);
并停止
CVDisplayLinkStop(displayLink);
完成后,请务必使用CVDisplayLinkRelease()
在创建的CVDisplayLink之后进行清理。
如果我可以发表最后一条评论,您认为CVDisplayLink通常与OpenGL配对的原因是您通常不希望使用Core Graphics在屏幕上快速刷新。如果您需要以30-60 FPS的速率制作动画,那么您将要么直接使用OpenGL进行绘制,要么使用Core Animation让它为您处理动画时序。核心图形绘制不是流畅地制作动画的方式。
答案 1 :(得分:1)
我认为NSTimer会成为这里的方式。你的陈述“它没有相同的准确性”,很有意思。也许你错误的准确性问题是运行循环模式问题。如果您在执行某些操作(例如打开菜单或拖动)时看到计时器未激活,则可能只需更改运行计时器的运行循环模式。
另一种选择是根据渲染的实际时间来计算动画,而不是根据自上次渲染过程后的假设差异来计算动画。做后者会在视觉上夸大渲染中可能会被忽视的任何延迟。
除了NSTimer,你还可以在GCD中做计时器。看一下这个例子:http://www.fieryrobot.com/blog/2010/07/10/a-watchdog-timer-in-gcd/
我们使用CVDisplayLink进行OpenGL渲染,包括视频播放。我很确定你想要做的事情有点过分了。