objective c - lottie - Alternativa de CADisplayLink para Mac OS X
lottie ios (2)
Si iOS existe CADisplayLink, en Mac OS X existe CVDisplayLink, pero no puedo encontrar la manera de usarlo, todos los ejemplos están relacionados con OpenGL.
Creé esta UIView personalizada y quiero traducirla a 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 que no puedo traducir es esta
- (void)baseInit {
_starImage = nil;
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(runLoop)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
Sé que puedo usar un NSTimer, pero no tiene la misma precisión
Pensaría que un NSTimer sería la manera de ir aquí. Su afirmación "no tiene la misma precisión", es interesante. Tal vez lo que está confundiendo con un problema de precisión es un problema de modo de bucle de ejecución. Si ve que el temporizador no se dispara cuando está haciendo ciertas cosas, como abrir menús o arrastrar, es posible que necesite cambiar los modos de ciclo de ejecución en los que se ejecuta el temporizador.
Otra opción es calcular su animación en función del tiempo real en el que se representa, no en una diferencia supuesta desde el último pase de representación. Hacer esto último exagerará visualmente cualquier demora en el procesamiento que de otro modo pasaría desapercibido.
Además de NSTimer, también puedes usar temporizadores en GCD. Eche un vistazo a este ejemplo: http://www.fieryrobot.com/blog/2010/07/10/a-watchdog-timer-in-gcd/
Usamos CVDisplayLink para renderizado OpenGL que incluye reproducción de video. Estoy bastante seguro de que es excesivo para lo que estás tratando de hacer.
Puede configurar CVDisplayLink para que funcione independientemente de OpenGL. El siguiente es un código que he usado para configurar un CVDisplayLink para desencadenar la captura y renderización regulares desde una cámara industrial:
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);
mi función renderCallback
parece a esto:
static CVReturn renderCallback(CVDisplayLinkRef displayLink,
const CVTimeStamp *inNow,
const CVTimeStamp *inOutputTime,
CVOptionFlags flagsIn,
CVOptionFlags *flagsOut,
void *displayLinkContext)
{
return [(__bridge SPVideoView *)displayLinkContext renderTime:inOutputTime];
}
Tendrás que reemplazar lo anterior con tu propia clase y método de devolución de llamada, por supuesto. El __bridge
en el código está ahí para compatibilidad con ARC, por lo que es posible que no lo necesite en un entorno contado de referencia manual.
Para comenzar a capturar eventos desde el enlace de visualización, usaría
CVDisplayLinkStart(displayLink);
y parar
CVDisplayLinkStop(displayLink);
Cuando haya terminado, asegúrese de limpiar después de su CVDisplayLink creado usando CVDisplayLinkRelease()
.
Si puedo hacer un último comentario, la razón por la que ve CVDisplayLink normalmente emparejado con OpenGL es que generalmente no desea hacer actualizaciones rápidas en la pantalla usando Core Graphics. Si necesita animar algo a una velocidad de 30-60 FPS, o bien va a querer dibujar directamente usando OpenGL o usar Core Animation y dejar que maneje el tiempo de animación para usted. El dibujo de Core Graphics no es la manera de animar las cosas de manera fluida.