2013-01-04 19 views
12

È iOS c'è CADisplayLink, in Mac OS X c'è CVDisplayLink, ma non riesco a trovare un modo per usarlo, tutti gli esempi sono relativi a OpenGL.Alternativa di CADisplayLink per Mac OS X

ho creato questa usanza UIView e voglio tradurlo in una 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 

La parte che non riesco a tradurre è questa

- (void)baseInit { 
    _starImage = nil; 
    CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self  selector:@selector(runLoop)]; 
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; 
} 

So che posso utilizzare un NSTimer, ma non ha la stessa accuratezza

risposta

19

È possibile configurare un CVDisplayLink per funzionare indipendentemente da OpenGL. Quello che segue è il codice che ho usato per impostare un CVDisplayLink per innescare la cattura e rendering regolare da una telecamera industriale:

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); 

mia funzione renderCallback sembra qualcosa di simile:

static CVReturn renderCallback(CVDisplayLinkRef displayLink, 
           const CVTimeStamp *inNow, 
           const CVTimeStamp *inOutputTime, 
           CVOptionFlags flagsIn, 
           CVOptionFlags *flagsOut, 
           void *displayLinkContext) 
{ 
    return [(__bridge SPVideoView *)displayLinkContext renderTime:inOutputTime]; 
} 

Avrete devi sostituire quanto sopra con il tuo metodo di classe e callback, ovviamente. Il codice __bridge è disponibile per il supporto ARC, quindi potrebbe non essere necessario in un ambiente conteggiato di riferimento manuale.

per iniziare a catturare gli eventi dal link di visualizzazione, utilizza

CVDisplayLinkStart(displayLink); 

e di smettere di

CVDisplayLinkStop(displayLink); 

Al termine, assicurarsi di pulire dopo il vostro creato CVDisplayLink utilizzando CVDisplayLinkRelease().

Se posso fare un ultimo commento, il motivo per cui si vede CVDisplayLink normalmente accoppiato con OpenGL è che di solito non si vuole fare rinfresca rapidi sullo schermo utilizzando core grafico. Se devi animare qualcosa a una velocità FPS di 30-60, vuoi disegnare direttamente usando OpenGL o utilizzare Core Animation e lasciare che gestisca il timing dell'animazione per te. Il disegno di Core Graphics non è il modo di animare fluidamente le cose.

+0

'return [(__bridge SPVideoView *) displayLinkContext renderTime: inOutputTime]; 'Non ho capito questa riga, suppongo che invece di SPVideoView dovrei usare MyView, ma non capisco cosa dovrei usare invece di renderTime . Potresti spiegarmi lo scopo di queste funzioni (sia renderCallback che renderTime)? – AR89

+0

@ AR89 - Poiché CVDisplayLink accetta una funzione di callback, non un metodo, sta a te richiamare un metodo appropriato nella tua classe. In quanto sopra, la classe che stavo usando è SPVideoView, e ho un metodo che prende il timestamp inOutputTime. Dovresti utilizzare questo timestamp per determinare se vuoi ancora attivare l'aggiornamento del tuo display. Per inciso, vorrei anche sottolineare che dovresti stare attento con gli aggiornamenti del display usando questo, perché questo callback si verifica su un thread in background, quindi dovrai assicurarti che il tuo disegno si verifichi sul thread principale se usi Core Graphics. –

+0

questa è una buona risposta, ma è importante che si finisca con il callback attivato su un thread diverso da dove verrebbe disegnata una vista? penso che potrebbe essere almeno per il rendering OpenGL. – jheriko

1

Penso che un NSTimer sarebbe il modo di andare qui. La tua affermazione "non ha la stessa accuratezza", è interessante. Forse ciò che si sta scambiando per un problema di accuratezza è un problema di modalità ciclo di esecuzione. Se stai vedendo il timer non sparare mentre stai facendo determinate cose come aprire i menu o trascinare, potresti dover cambiare le modalità del ciclo di esecuzione in cui è in esecuzione il timer.

Un'altra opzione è calcolare l'animazione basata sul tempo effettivo in cui è stato reso, non su una presunta differenza dall'ultimo passaggio di rendering. Facendo quest'ultimo, esagerare visivamente eventuali ritardi nel rendering che potrebbero altrimenti passare inosservati.

Oltre a NSTimer, è anche possibile eseguire i timer in GCD. Date un'occhiata a questo esempio: http://www.fieryrobot.com/blog/2010/07/10/a-watchdog-timer-in-gcd/

Utilizziamo CVDisplayLink per il rendering OpenGL che include la riproduzione di video. Sono abbastanza sicuro che sia eccessivo per quello che stai cercando di fare.