iOS App multiple UIWindows become unresponsive to touch events after device rotation animation

I am developing an App. There are multiple windows in it. A main window that shows the main UI of the app, and some other util windows that are created dynamically when needed. All of these windows have their rootviewcontrollers, and they all support all the userinterface orientations. But when I rotate the device, the windows seemed rotate to the right orientation, but some window has four strange black rectangles along each side.

And the worse thing is that the whole app become freezed, it does not respond to touch event, I need to force quit it.

I did a lot of experiments to find out the cause of this issue in last two months. I found some truth about this issue:

Good Case:

  1. If the App only has the main window, the issue gone.
  2. If the rotation animation completed, the issue gone.

the attachment shows some callstacks when rotation animation completed.

Bad Case:

When the issue occurs, I find out the rotation animation does not complete.

This is some code of rootviewcontroller:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
    
    
    NSLog(@"%s, size: %@", __PRETTY_FUNCTION__, NSStringFromCGSize(size));
    NSLog(@"%@", NSThread.callStackSymbols);
    
    NSLog(@"%@", self.view);
    NSLog(@"%@", self.view.subviews);
    
    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        NSLog(@"animation block");
        fprintf(stderr, "stderr animation block\n");
        fprintf(stdout, "stdout animation block\n");
        NSLog(@"%@", NSThread.callStackSymbols);

        NSLog(@"animation block SDL_uikitviewcontroller runloop mode: %@", NSRunLoop.currentRunLoop.currentMode);
        
        NSLog(@"%@", self.view);
        NSLog(@"%@", self.view.subviews);
    } completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        
        NSLog(@"completion block, isCancelled: %@", context.isCancelled ? @"YES" : @"NO");
        fprintf(stderr, "stderr animation completion block\n");
        fprintf(stdout, "stdout animation completion block\n");
        NSLog(@"%@", NSThread.callStackSymbols);

        NSLog(@"animation complete block SDL_uikitviewcontroller runloop mode: %@", NSRunLoop.currentRunLoop.currentMode);
    }];

    [coordinator notifyWhenInteractionChangesUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        NSLog(@"%s", __PRETTY_FUNCTION__);
    }];
    
}

I guess this issue is similar to another common issue: UINavigationController push/pop viewcontroller when previous push/pop animation is running, or showing a UIAlertController when viewcontroller is being pushing/popping

They all have the same result, the app become unresponsive to touch events, but they can respond to other events(I did some test, the app is still running, not in a deadlock state, it just can not respond to touch events)

I guess the root cause of this issue is the animations conflict, or the view hierarchy changed when animating, so the animation states and the view hierarchy were messed up.

But I don't know how to fix it.

I have a workaround, but it has side effect.

I found in the Bad Case, some CAAnimations even not start.

I hooked -[CALayer addAnimation:forKey:], -[UIViewAnimationState animationDidStart], and -[UIViewAnimationState animationDidStop:finished:] methods, and log the info about the layers and animations. By comparing the logs between the good case and bad case, I found that the animations are triggered by the Runloop's CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE function In the bad case, the runloop is running in different mode(actually speaking, in different state, they all run in default mode)

iOS App multiple UIWindows become unresponsive to touch events after device rotation animation
 
 
Q