Non riesco a parlare con le differenze di iOS 5.xv 6.x, ma quando uso CADisplayLink
, non ho mai codice rigido come "sposta x pixel/punti" ogni iterazione, ma piuttosto guardo lo timestamp
(o più esattamente, il delta tra il mio timestamp
iniziale e l'attuale timestamp
) e calcolare la posizione in base al tempo trascorso, non dal numero di frame che sono passati. In questo modo, i frame rate non influenzano la velocità del movimento, ma piuttosto la sua fluidità. (E la differenza tra il 30 e il 29 è probabile che sia indistinguibile.)
Per citare dal CADisplayLink Class Reference:
Una volta che il collegamento del display è associata ad un ciclo di esecuzione, il selettore sul bersaglio viene chiamato quando i contenuti dello schermo devono essere aggiornati. L'obiettivo può leggere la proprietà timestamp del collegamento di visualizzazione per recuperare l'ora in cui è stato visualizzato il frame precedente. Ad esempio, un'applicazione che visualizza filmati potrebbe utilizzare il timestamp per calcolare quale fotogramma video verrà visualizzato successivamente. Un'applicazione che esegue le proprie animazioni potrebbe utilizzare il timestamp per determinare dove e come gli oggetti visualizzati vengono visualizzati nel frame successivo. La proprietà duration fornisce la quantità di tempo tra i frame. È possibile utilizzare questo valore nell'applicazione per calcolare la frequenza fotogrammi del display, il tempo approssimativo in cui verrà visualizzato il fotogramma successivo e per regolare il comportamento del disegno in modo che il fotogramma successivo sia preparato in tempo per essere visualizzato.
Come esempio a caso, here che sto animare un UIBezierPath
utilizzando il numero di secondi trascorsi come parametro.
O, in alternativa, se hai a che fare con una sequenza di fotogrammi UIImage
, si potrebbe calcolare il numero di telaio come segue:
@property (nonatomic) CFTimeInterval firstTimestamp;
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
if (!self.firstTimestamp)
self.firstTimestamp = displayLink.timestamp;
CFTimeInterval elapsed = (displayLink.timestamp - self.firstTimestamp);
NSInteger frameNumber = (NSInteger)(elapsed * kFramesPerSecond) % kMaxNumberOfFrames;
// now do whatever you want with this frame number
}
O, meglio ancora, per evitare il rischio di perdere un frame, andare avanti e lascia che funzioni a 60 fps e determini solo se il frame deve essere aggiornato e in questo modo ridurrai il rischio di far cadere un frame.
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
if (!self.firstTimestamp)
self.firstTimestamp = displayLink.timestamp;
CFTimeInterval elapsed = (displayLink.timestamp - self.firstTimestamp);
NSInteger frameNumber = (NSInteger)(elapsed * kFramesPerSecond) % kMaxNumberOfFrames;
if (frameNumber != self.lastFrame)
{
// do whatever you want with this frame number
...
// now update the "lastFrame" number property
self.lastFrame = frameNumber;
}
}
Ma spesso, numeri di frame non sono necessari affatto.Ad esempio, per spostare un UIView
in un cerchio, si potrebbe fare qualcosa di simile:
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
if (!self.firstTimestamp)
self.firstTimestamp = displayLink.timestamp;
CFTimeInterval elapsed = (displayLink.timestamp - self.firstTimestamp);
self.animatedView.center = [self centerAtElapsed:elapsed];
}
- (CGPoint)centerAtElapsed:(CFTimeInterval)elapsed
{
CGFloat radius = self.view.bounds.size.width/2.0;
return CGPointMake(radius + sin(elapsed) * radius,
radius + cos(elapsed) * radius);
}
A proposito, se si utilizzano strumenti per misurare il frame rate, può sembrare più lento di quanto in realtà sarà in un dispositivo. Al commento di Matt, per frequenze fotogrammi accurate, è necessario misurarlo a livello di codice su un dispositivo reale con una build di rilascio.
Grande idea! Quindi, nel metodo onTick, ho il timestamp, poi il prossimo, ho il timestamp di nuovo - quindi confrontare per vedere se ottengo la differenza di 0.03333 secondi? Cosa succede se non lo faccio? Puoi approfondire il tuo concetto? – objectiveccoder001
@ objectiveccoder001 Se c'è qualche ragione per cui hai assolutamente bisogno di 30 fps, allora sì, potresti fare qualcosa del genere. Vedere il mio esempio di codice che può determinare il numero di frame in base al tempo trascorso. Molte animazioni, tuttavia, non è necessario, ma è sufficiente calcolare la nuova posizione in base al tempo trascorso e non c'è alcun motivo per cui è vincolato a 30 fps (vs 29 vs 59 vs ecc.). – Rob
FANTASTICO! Grazie mille. Cosa sono kMaxNumberOfFrames? – objectiveccoder001