【问题标题】:CADisplaylink code fires even when display doesn't refresh即使显示不刷新,CADisplaylink 代码也会触发
【发布时间】:2013-06-10 15:55:22
【问题描述】:

注意:从 iOS7 开始,此问题可能仅在模拟器中出现 -- 仍在测试中。

我有一个 CADisplayLink 的实现,当且仅当显示实际刷新时,我才需要运行代码

这不会发生。

这是一个非常简单的测试程序:

我开始运行显示链接;在第一帧 aLabel 应该显示“WordFlash”;对于接下来的 19 帧,它应该显示“--------”,对于接下来的 100 帧,它应该是空白的;那么这个循环应该重复。

偶尔(比如 8 次中有 1 次),并且出乎意料的是,屏幕不会刷新以显示“WordFlash”,尽管代码确实已经触发(因为计数器已经前进)。仅当“WordFlash”已成功显示 1 帧时,我才需要计数器前进。

有什么想法吗?我完全被难住了。

注意:这种显示刷新跳过似乎与设备执行简单代码所需的时间无关(如在 NSLogged time-to-execute 中,代码可以在两个不同的周期中相同,而只有一个循环已经成功闪出单词了。

    #import "HomePwnerViewController.h"


@interface HomePwnerViewController ()

@end

@implementation HomePwnerViewController {
    int counter;
    UILabel *aLabel;

}

- (void)viewDidLoad
{
    [super viewDidLoad];

    aLabel = [[UILabel alloc] initWithFrame:self.view.frame];

    [self.view addSubview:aLabel];

    aLabel.alpha = 1;

    aLabel.text = @"";

    aLabel.textAlignment = NSTextAlignmentCenter;

    [aLabel setFont:[UIFont fontWithName:@"Courier" size:50]];

  [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(testWordFlashMethod) userInfo:nil repeats:NO];

}

- (void) displaySensitiveProcedure: (CADisplayLink *) sender {

    CFTimeInterval startTime = CACurrentMediaTime();


    if (counter == 0)
        aLabel.text = @"FlashWord";
    else if (counter < 20)
        aLabel.text = @"---------";
    else
        aLabel.text = @"";

    CFTimeInterval endTime = CACurrentMediaTime();
    if (counter == 0)
        NSLog(@"frame time %f frame length %f ratio %f", endTime - startTime, sender.duration, (endTime - startTime)/sender.duration);

    counter++;

    if (counter == 120)
        counter = 0;
}

- (void) testWordFlashMethod {
    CADisplayLink *DL = [CADisplayLink displayLinkWithTarget:self selector:@selector(displaySensitiveProcedure:)];

    DL.frameInterval = 1;

    counter = 0;

    [DL addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];   
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

非常感谢,

b

【问题讨论】:

  • 你为什么不使用-setNeedsDisplay?
  • 您在评论中提到了aLabel,但在代码中却没有。为什么?
  • 感谢@robmayoff 将我的 aLabel 留在了我的示例代码中。
  • @nielsbot 包括 [aLabel setNeedsDisplay];没有摆脱错误 - 已在代码中更新
  • 嗯,想知道这是否只是模拟器的问题......

标签: cadisplaylink


【解决方案1】:

如果我猜的话,我会说在屏幕刷新时为 UILabel 设置动画并不是最好的方法。根据我的经验,更改它们上的文本可能需要一些时间来呈现。如果您想在屏幕上绘制破折号,最好使用 Core Graphics API 或基于 CALayer 的绘图方法。

【讨论】:

  • 是的,重点不是按照描述的那样制作动画。这只是隔离了我在一个更大的程序中遇到的问题。 IE 我的最终目标不是在屏幕上设置破折号的动画,而是当且仅当 uilabel 成功重绘时触发代码。
【解决方案2】:

您的 displaySensitiveProcedure 可能需要超过一帧 (16.7 毫秒) 来更新屏幕,这使其跳过一帧。如果您持续花费更多时间(例如 20 毫秒),您可以减少 DisplayLink 的触发频率,以便以较慢的帧速率获得流畅的动画 - 请参阅 frameInterval 属性。我建议您通过执行以下操作来计时您的回调:

- (void) displaySensitiveProcedure: (CADisplayLink *) sender { 
  CFTimeInterval startTime = CACurrentMediaTime();

  ... <code> ...

  CFTimeInterval endTime = CACurrentMediaTime();
  NSLog(@"frame time: %f", endTime - startTime);
}

【讨论】:

  • 测试代码执行时间的好主意。但是代码执行(或记录的帧时间)发生在 0.00002 到 0.00005 秒之间,所以屏幕应该有足够的时间来进行更改,不是吗?
  • 是的,这看起来又好又快。也许在这个回调之外发生了来自 UIKit 的绘图?每当我使用 CADisplayLink 时,我一直在手动绘制/移动 CALayers。
  • 另外,不要对正在测试性能的代码进行 nslog - nslog 似乎每次调用(在设备上)需要大约 2 毫秒,并且完全会影响性能。
猜你喜欢
  • 1970-01-01
  • 2016-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多