ios - CADisplayLink 在 iOS5.1 上运行较低的帧率

标签 ios objective-c cadisplaylink

我在我的 iPhone 应用程序中使用 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 中的功能是否不同?任何可能的解决方法/解决方案?

最佳答案

我无法说出 iOS 5.x 和 6.x 之间的差异,但是当我使用 CADisplayLink 时,我从不在每次迭代中硬编码诸如“移动 x 像素/点”之类的东西,而是我查看 timestamp(或者更准确地说,我的初始 timestamp 和当前 timestamp 之间的差值)并根据多少来计算位置时间已经流逝,而不是经过了多少帧。这样,帧速率不会影响运动的速度,而只会影响运动的平滑度。 (而且 30 和 29 之间的区别很可能是无法区分的。)

引用自CADisplayLink Class Reference :

Once the display link is associated with a run loop, the selector on the target is called when the screen’s contents need to be updated. The target can read the display link’s timestamp property to retrieve the time that the previous frame was displayed. For example, an application that displays movies might use the timestamp to calculate which video frame will be displayed next. An application that performs its own animations might use the timestamp to determine where and how displayed objects appear in the upcoming frame. The duration property provides the amount of time between frames. You can use this value in your application to calculate the frame rate of the display, the approximate time that the next frame will be displayed, and to adjust the drawing behavior so that the next frame is prepared in time to be displayed.


作为一个随机示例,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
}

或者,更好的是,为了避免丢失帧的风险,继续并让它以 60 fps 的速度运行,然后确定帧是否需要更新,这样您就可以降低丢失帧的风险。

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

顺便说一句,如果您使用 Instruments 来测量帧速率,它看起来可能比在设备上的实际速度要慢。对于马特的评论,为了获得准确的帧速率,您应该在具有发布版本的实际设备上以编程方式测量它。

关于ios - CADisplayLink 在 iOS5.1 上运行较低的帧率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14961581/

相关文章:

ios - 跳跃的 CADisplayLink 动画

ios - 快速确定是否有足够的单元格来覆盖整个屏幕

iphone - 我如何将 CoreText 框架导入 xcode 3.1.4

ios - React native 人脸检测对我不起作用(iOS)

iphone - 使用 iPhone 的蜡笔效果

ios - 如何使用 for 循环生成按钮数组,然后使用 CADisplayLink 为它们设置动画

ios - 是否可以在没有用户交互的情况下关闭 MFMailComposeViewController?

ios - 我的 Firebase 观察者不会停止观察

ios - 将 NSArray 存储到 KeychainItemWrapper 中

ios - CADisplayLink 回调是否总是在主线程上运行?