iphone - UITableView 和 UIScrollview 停止 cocos2d 动画

标签 iphone ios objective-c uitableview cocos2d-iphone

我有 UITableView,它显示在我的层`[[CCDirector sharedDirector].view addSubview:myTableView] 前面,并占据了屏幕的一部分。

一切正常:它检测触摸。 但是在 tableView 的滚动动画期间,所有 cocos2d 都会卡住,直到 tableView 结束滚动。 (在卡住状态下,我的意思是所有节点的操作都暂停了)。

问题是:这种卡住的原因是什么,我该如何避免?


-(void) mainLoop:(id)sender
{
if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0)
{
    return;
}

if (self.useCocoaFriendlyRendering)
{
    dispatch_sync(frameRenderingDispatchQueue, ^{ // HERE dispatch_async make black screen, i shows everything but still bad performance.
        CCGLView *openGLview = (CCGLView*)[self view];
        [EAGLContext setCurrentContext:openGLview.context];
        [self drawScene];
        dispatch_semaphore_signal(frameRenderingSemaphore);
    });
}
else
{
    CCGLView *openGLview = (CCGLView*)[self view];
    [EAGLContext setCurrentContext:openGLview.context];
    [self drawScene];
    dispatch_semaphore_signal(frameRenderingSemaphore);
}
}

最佳答案

原因是因为 UIKit View 没有设计为与其他 View 协作。它们用于用户交互,因此滚动是否停止更新其他 View 通常并不重要。在用户界面驱动的应用程序中,这很有意义,因为您希望对具有用户焦点并且需要大量资源来制作动画(即滚动)的 View 提供最佳响应和感觉。

正确解决此问题的唯一方法是使用信号量改进 CCDirectorDisplayLink 中的 cocos2d 渲染循环:

-(void) mainLoop:(id)sender
{
    if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0)
    {
        return;
    }

    if (useCocoaFriendlyRendering)
    {
        dispatch_async(frameRenderingDispatchQueue, ^{
            [EAGLContext setCurrentContext:openGLView_.context];
            [self drawScene];
            dispatch_semaphore_signal(frameRenderingSemaphore);
        });
    }
    else
    {
        [EAGLContext setCurrentContext:openGLView_.context];
        [self drawScene];
        dispatch_semaphore_signal(frameRenderingSemaphore);
    }
}

此代码将 cocos2d drawscene 方法放入其自己的调度队列中。我曾经明白它做了什么,但我不记得了,所以与其错误地解释它的作用或它为什么起作用,我希望有人能在评论中补充这一点。 :)

注意:可能不需要每次都调用最后一个分派(dispatch)信号量,但我这样做是因为切换 cocoaFriendlyRendering 标志可能会导致 semaphore_wait 无限期等待(卡住渲染)。所以我每次都发出信号。

我更新了我的 CCDirectorDisplayLink 如下(CCDirectorIOS 有 cocoaFriendlyRendering BOOL):

@interface CCDirectorDisplayLink : CCDirectorIOS
{
    id displayLink;
    dispatch_semaphore_t frameRenderingSemaphore;
    dispatch_queue_t frameRenderingDispatchQueue;
}
-(void) mainLoop:(id)sender;
@end

在 startAnimation 中创建了信号量和调度队列:

- (void) startAnimation
{
        ...
    displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(mainLoop:)];
    [displayLink setFrameInterval:frameInterval];
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

    frameRenderingSemaphore = dispatch_semaphore_create(1);
    frameRenderingDispatchQueue = dispatch_queue_create("render", DISPATCH_QUEUE_SERIAL);
}

并在dealloc中释放:

-(void) dealloc
{
    dispatch_release(frameRenderingDispatchQueue);
    dispatch_release(frameRenderingSemaphore);
    [displayLink release];
    [super dealloc];
}

这样可以防止 ScrollView 遮挡cocos2d框架绘制。我添加了 cocoaFriendlyRendering BOOL(我假设您知道如何添加 BOOL 属性)所以我可以只为使用 ScrollView 的场景打开此行为。我认为这可能会更好地提高性能并防止游戏过程中出现任何奇怪的问题,但这可能没有必要。

此代码适用于 cocos2d 1.0.1,可能需要稍作调整以适用于 cocos2d 2.x,例如 EAGLView -> CCGLView、EAGLContext -> CCGLContext。

Other solutions revolve around timers和/或降低帧速率(animationInterval)和/或更改显示链接对象的运行循环模式。它们并不能很好地工作,滚动可能不流畅或滚动可能突然停止。我尝试了所有这些解决方案,因为我不想弄乱 cocos2d 的渲染循环 - 但信号量是唯一可以使 UIKit 滚动与 cocos2d 协作(反之亦然)的完美工作解决方案。

更新:

我忘了说,我做了一个处理 cocoaFriendlyRendering 的 UIScrollView 的子类(在 init 上是 YES,在 dealloc 上不是)更重要的是,它覆盖了触摸事件,以便根据需要将它们转发到 cocos2d(每当 ScrollView 不存在时) t 滚动)。

这些是重写的触摸方法:

-(void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
    if (self.dragging)
        [super touchesBegan:touches withEvent:event];
    else
        [[CCDirector sharedDirector].openGLView touchesBegan:touches withEvent:event];
}

-(void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
    if (self.dragging)
        [super touchesMoved:touches withEvent:event];
    else
        [[CCDirector sharedDirector].openGLView touchesMoved:touches withEvent:event];
}

-(void) touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event
{
    [super touchesCancelled:touches withEvent:event];
    [[CCDirector sharedDirector].openGLView touchesCancelled:touches withEvent:event];
}

-(void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
    [super touchesEnded:touches withEvent:event];
    [[CCDirector sharedDirector].openGLView touchesEnded:touches withEvent:event];
}

关于iphone - UITableView 和 UIScrollview 停止 cocos2d 动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15185720/

相关文章:

iphone - objective-c 中的错误处理与异常处理

iphone - 我如何在导航栏上实现这一点

iPhone UITableView 单元格获取 'stuck' 并与快速滚动混合

iphone - 如何在 subview 中处理 UITapGestureRecognizer?

ios - 在不阻止清除按钮的情况下禁用文本字段编辑

ios - 如何使用后退按钮在 UINavigationBar 的左侧添加多个 UIBarButtonItem

objective-c - 在 NSMutableArray 中使用 insertObject 时发生崩溃

ios - 将签名身份和临时配置文件从一台 Mac 导出和导入到另一台 Mac

ios - 如何指定多种类型供委托(delegate)引用?

ios - 隐藏键盘ios