UITableView & UIScrollview stop cocos2d animations

后端 未结 2 1323
情书的邮戳
情书的邮戳 2021-01-06 13:34

I have UITableView, which is shown in front of my Layer `[[CCDirector sharedDirector].view addSubview:myTableView] and takes some part of the screen.

Ev

相关标签:
2条回答
  • 2021-01-06 13:43

    The reason is because UIKit views are not designed to be cooperative with other views. They're for user interaction, and so it usually doesn't matter if scrolling stops updating other views. In a user-interface driven app that makes a lot of sense because you want to give the best response and feel to the view that has the user's focus and needs a lot of resources to animate (ie scrolling).

    The only way to fix this properly is to improve the cocos2d render loop in CCDirectorDisplayLink with a semaphore:

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

    This code puts the cocos2d drawscene method in its own dispatch queue. I once understood what it did but I can't remember it anymore, so instead of incorrectly explaining what it does or why it works I'm hoping someone can fill this in in a comment. :)

    Note: the last dispatch semaphore may not need to be called every time, but I did because switching the cocoaFriendlyRendering flag might otherwise cause the semaphore_wait to wait indefinitely (freezes rendering). So I just signal it every time.

    I updated my CCDirectorDisplayLink as follows (CCDirectorIOS has the cocoaFriendlyRendering BOOL):

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

    In startAnimation the semaphore and dispatch queue are created:

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

    And released in dealloc:

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

    This prevents the scroll view from blocking the cocos2d frame drawing. I added the cocoaFriendlyRendering BOOL (I assume you know how to add a BOOL property) so I could turn this behavior on only for scenes that do use scroll views. I thought this might be better for performance and to prevent any odd issues during gameplay, but it's probably not necessary.

    This code is for cocos2d 1.0.1 and might need to be slightly adapted to work with cocos2d 2.x, for example EAGLView -> CCGLView, EAGLContext -> CCGLContext.

    Other solutions revolve around timers and/or reducing the framerate (animationInterval) and/or changing the run loop mode of the display link object. They don't really work too well, scrolling might not be smooth or scrolling might suddenly stop. I tried all of those solutions because I didn't want to mess with cocos2d's render loop - but the semaphore was the only flawlessly working solution to make UIKit scrolling cooperative with cocos2d (and vice versa).

    UPDATE:

    I forgot to mention, I made a subclass of UIScrollView that handles cocoaFriendlyRendering (YES on init, NO on dealloc) and more importantly, it overrides the touch events as to forward them to cocos2d as needed (whenever the scroll view isn't scrolling).

    These are the overridden touch methods:

    -(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];
    }
    
    0 讨论(0)
  • 2021-01-06 13:56

    Go into the CCDirector.m OR CCDirectorIOS.m file and find this line:

    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    

    Change it to this:

    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    
    0 讨论(0)
提交回复
热议问题