18

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,但它没有相同的准确性

4

2 回答 2

27

您可以将 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];
}

当然,您必须用您自己的类和回调方法替换上面的内容。代码中的__bridgeARC 支持,因此在手动引用计数环境中您可能不需要它。

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

CVDisplayLinkStart(displayLink);

并停止

CVDisplayLinkStop(displayLink);

完成后,请务必在创建 CVDisplayLink 后使用CVDisplayLinkRelease().

如果我可以发表最后一条评论,您看到 CVDisplayLink 通常与 OpenGL 配对的原因是您通常不希望使用 Core Graphics 在屏幕上进行快速刷新。如果您需要以 30-60 FPS 的速率制作动画,您要么想直接使用 OpenGL 进行绘制,要么使用 Core Animation 并让它为您处理动画时间。Core Graphics 绘图不是流畅地动画化事物的方式。

于 2013-01-04T22:12:54.030 回答
1

我认为 NSTimer 将是去这里的方式。您的说法“它没有相同的准确性”,很有趣。也许您误认为精度问题是运行循环模式问题。如果您在执行某些操作(例如打开菜单或拖动)时看到计时器没有触发,您可能只需要更改计时器正在运行的运行循环模式。

另一种选择是根据其渲染的实际时间来计算您的动画,而不是根据自上次渲染通道以来的假设差异。执行后者会在视觉上夸大渲染中的任何延迟,否则这些延迟可能会被忽视。

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

我们使用 CVDisplayLink 进行包括视频播放的 OpenGL 渲染。我很确定这对于您正在尝试做的事情来说太过分了。

于 2013-01-04T20:35:40.913 回答