用于Mac OS X的CADisplayLink的替代方法

iOS有CADisplayLink,Mac OS X有CVDisplayLink,但是我找不到使用它的方法,所有的例子都和OpenGL有关。

我创build了这个自定义的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,但它不具有相同的准确性

您可以将CVDisplayLinkconfiguration为独立于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]; } 

当然,你必须用自己的类和callback方法replace上面的代码。 代码中的__bridge是支持ARC的,因此您可能不需要在手动引用计数的环境中。

要开始从显示链接捕捉事件,你可以使用

 CVDisplayLinkStart(displayLink); 

并停下来

 CVDisplayLinkStop(displayLink); 

完成后,确保使用CVDisplayLinkRelease()在创build的CVDisplayLink之后进行清理。

如果我可以做最后一个评论,你看到CVDisplayLink通常与OpenGL配对的原因是,你通常不希望使用Core Graphics在屏幕上进行快速刷新。 如果您需要以30-60 FPS的速度进行animation制作,您可以直接使用OpenGL进行绘制,也可以使用Core Animation,然后让它为您处理animation计时。 核心graphics绘制不是stream畅地绘制animation的方法。

我认为NSTimer将成为这里的一种方式。 您的陈述“它不具有相同的准确性”,很有趣。 也许你错误的准确性问题是一个运行循环模式的问题。 如果在执行某些操作(例如打开菜单或拖动)时看到计时器未触发,则可能需要更改计时器正在运行的运行循环模式。

另一种select是根据渲染的实际时间来计算animation,而不是自上次渲染通过以来的假定差异。 做后者会在视觉上夸大延迟渲染,否则可能会被忽视。

除了NSTimer,你也可以在GCD中做定时器。 看看这个例子: http : //www.fieryrobot.com/blog/2010/07/10/a-watchdog-timer-in-gcd/

我们使用CVDisplayLink进行OpenGL渲染,包括video播放。 我很确定这是对你正在做的事情的矫枉过正。