2013-02-19 2 views
6

내 iPhone 앱에서 CADisplayLink을 사용하고 있습니다.iOS5.1에서 더 낮은 프레임 속도로 실행되는 CADisplayLink

SMPTELink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onTick)]; 
SMPTELink.frameInterval = 2;//30fps 60/n = fps 
[SMPTELink addToRunLoop:[NSRunLoop mainRunLoop] 
        forMode:NSDefaultRunLoopMode]; 

onTick 따라서 30FPS (제 1/30)의 프레임마다 호출된다

여기 관련 코드이다. 이것은 iOS6 +에서 효과가 있습니다. 정확히 내가 필요한 것을 수행합니다. 그러나 iOS5.1을 실행하는 iPhone 4s에서 앱을 실행했을 때 onTick 메서드가 iOS6보다 약간 느리게 실행되었습니다. 거의 그것을 실행하고 있었던 것처럼 29FPS. 조금 지나면 iOS6 iPhone 5와 동기화되지 않았습니다.

onTick 메서드의 코드는 시간이 많이 걸리지 않습니다 (내 생각 중 하나였습니다 ...). iPhone이 아니기 때문에 iOS6를 실행하는 iPhone 4s에서 제대로 실행됩니다.

CADisplayLinkiOS5.1에서 다르게 작동합니까? 가능한 해결 방법/솔루션은 무엇입니까?

답변

16

iOS 5.xv 6.x의 차이점은 말할 수 없지만 CADisplayLink을 사용하면 모든 반복마다 "x 픽셀/점 이동"과 같은 코드를 하드 코딩하지는 않지만, 대신 timestamp (또는 더 정확하게는 내 timestamp과 현재 timestamp 사이의 델타)를 확인하고 경과 된 프레임 수를 기준으로 위치를 계산합니다. 그런 식으로 프레임 속도는 모션의 속도에 영향을 미치지 않고 단지 부드러움에 영향을줍니다. (그리고 30과 29의 차이는 구별 될 가능성이 높습니다.)

CADisplayLink Class Reference에서 인용하자면 :

디스플레이 링크가 실행 루프와 연관되면, 대상의 선택이 될 때 호출되는 화면의 내용을 업데이트해야합니다. 대상에서 디스플레이 링크의 timestamp 속성을 읽으면 이전 프레임이 표시된 시간을 검색 할 수 있습니다. 예를 들어, 동영상을 표시하는 응용 프로그램은 타임 스탬프를 사용하여 다음에 표시 할 비디오 프레임을 계산할 수 있습니다. 자체 애니메이션을 수행하는 응용 프로그램은 시간 소인을 사용하여 다가오는 프레임에 표시되는 객체의 위치와 방법을 결정할 수 있습니다. duration 속성은 프레임 사이의 시간을 제공합니다. 응용 프로그램에서이 값을 사용하여 디스플레이의 프레임 속도, 다음 프레임이 표시 될 대략적인 시간을 계산하고 다음 프레임이 표시되도록 준비 될 수 있도록 그리기 동작을 조정할 수 있습니다. 임의의 예를 들어


, here 나는 매개 변수로 경과 한 시간 (초)을 사용하여 UIBezierPath 애니메이션을하고있다. 당신이 UIImage 프레임의 순서를 처리하는 경우 다음과 같이

또는이 양자 택일로, 당신은 프레임 수를 계산 수 :

@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 
} 

을 프레임 손실 위험 방지하기 위해, 또는 더 나은 아직, 진행 이 작업을 60fps로 실행하고 프레임을 업데이트해야하는지 여부를 결정하면 프레임을 놓을 위험이 줄어 듭니다.

- (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; 
    } 
} 

그러나 자주는, 프레임 번호는 전혀 필요하지 않습니다.예를 들어, 원에 UIView를 이동, 당신은 같은 것을 할 수 있습니다 당신은 프레임 속도를 측정하는 계측기를 사용하는 경우, 그런데

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

을, 그것이 정말로 것보다 느리게 보일 수 있습니다 장치. 정확한 프레임 속도를 얻으려면 matt의 설명에 따라 릴리스 빌드가있는 실제 장치에서 프로그래밍 방식으로 측정해야합니다.

+0

멋진 아이디어! 따라서 onTick 메서드에서 타임 스탬프를 설정 한 다음 다음 타임 스탬프에 다시 타임 스탬프를 적용한 다음 0.03333 초 차이가 나는지 비교해보십시오. 내가하지 않으면? 당신은 당신의 개념에 대해 자세히 설명 할 수 있습니까? – objectiveccoder001

+0

@ objectiveccoder001 절대적으로 30fps가 필요한 몇 가지 이유가 있다면, 그렇습니다. 그렇게 할 수 있습니다. 경과 한 시간을 기준으로 프레임 번호를 결정할 수있는 코드 샘플을 참조하십시오. 그러나 많은 애니메이션은 그렇게 할 필요가 없지만 경과 시간을 기준으로 새로운 위치를 계산할 수 있으며 30fps (29 vs 59 vs 등)로 제한되는 이유가 없습니다. – Rob

+0

굉장! 정말 고맙습니다. kMaxNumberOfFrames는 무엇입니까? – objectiveccoder001

3

롭의 대답은 정확합니다. CADisplayLink의 프레임 속도에 대해 걱정하지 않아도됩니다. 실제로 타이머가 규칙 성 같은 것으로 발사 될 것이라고 예상해서는 안됩니다. 당신의 임무는 원하는 시간 스케일에 따라 원하는 애니메이션을 나눠서 누적 된 타임 스탬프를 더함으로써 타이머가 작동 할 때마다 실제로 얻는 프레임을 그리는 것입니다. _frame 값이 (우리가 애니메이션을 시작)과 1 (우리가 애니메이션을 완료) 0 사이에 실행하고, 즉 코드에서

if (self->_timestamp < 0.01) { // pick up and store first timestamp 
    self->_timestamp = sender.timestamp; 
    self->_frame = 0.0; 
} else { // calculate frame 
    self->_frame = sender.timestamp - self->_timestamp; 
} 
sender.paused = YES; // defend against frame loss 

[_tran setValue:@(self->_frame) forKey:@"inputTime"]; 
CGImageRef moi3 = [self->_con createCGImage:_tran.outputImage 
            fromRect:_moiextent]; 
self->_iv.image = [UIImage imageWithCGImage:moi3]; 
CGImageRelease(moi3); 

if (self->_frame > 1.0) { 
    [sender invalidate]; 
    self->_frame = 0.0; 
    self->_timestamp = 0.0; 
} 
sender.paused = NO; 

: 여기

샘플 내 책 코드 중간이 프레임을 그리는 데 필요한 특정 상황을 처리합니다. 애니메이션을 길게 또는 짧게 만들려면 _frame ivar을 설정할 때 축척 비율을 곱하면됩니다.

결과는 전혀 의미가 없기 때문에 시뮬레이터에서 테스트하지 않아야합니다. 장치 만 CADisplayLink를 올바르게 실행합니다.

(예 : http://www.apeth.com/iOSBook/ch17.html#_cifilter_transitions)