Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent the flash when calling drawViewHierarchyInRect:afterScreenUpdates: on iOS 8?

Under iOS 8 a call to UIView's

[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];

results in a 1 frame flash of the image. You can see it for a split second on the screen. It only happens when afterScreenUpdates parameter is YES.

This is my complete code to take screenshot:

UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, sf);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
CGContextConcatCTM(ctx, [self.layer affineTransform]);

if ([self respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) { // iOS 7+
    [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
} else { // iOS 6
    [self.layer renderInContext:ctx];
}

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

CGContextRestoreGState(ctx);
UIGraphicsEndImageContext();

Is there a workaround?

like image 316
iamjustaprogrammer Avatar asked Sep 20 '14 11:09

iamjustaprogrammer


2 Answers

Provide @3x launch images to be used in iPhone6 and iPhone6 plus and this problem goes away.

like image 92
SarpErdag Avatar answered Nov 16 '22 06:11

SarpErdag


Here's a possible workaround that solved my issue.

Use CATransaction.completionBlock to call UIView drawViewHierarchyInRect:afterScreenUpdates with the screen updates flag set to NO. I used this to workaround the same problem. It is semantically equivalent to setting the flag to YES in my case since I'm only invoking it after the current CATransaction has finished and the screen is in the desired state. That said, in my case, I don't have any animations, so this is pretty much immediate and captures exactly what I want. If there were animations, this may not be appropriate.

For my case, I have a collection view that is a collection of files. Each item is rendered as a snapshot of what the file looks like once the user opens it. Some files cause the UI to have hundreds of layers. When these complicated views were being snapshotted, I would get a black screen flash; or the last/current animation to partially rerun. My collection view is w/in a navigation controller, I was seeing partial reruns of the pop animation. I also have noticed reruns of rotation animations. For simpler files, there was often no issue. I noticed this on my iPad Air (iOs 8.1) and on various simulators. I tried putting appIcons and launchImages in place but they had no effect.

Here was the original code. It initializes a view controller, then adds the controller's view to the a utility view.

UIViewController *vlController = [[ComplicatedViewController alloc]init];
UIView *innerView = vlController.view;
[v addSubview:innerView];
innerView.transform = CGAffineTransformMakeScale(scalar, scalar);
innerView.center = CGRectCenterPoint(v.bounds);
auto sz = v.bounds.size;
innerView.bounds = {{0,0}, {sz.width/scalar, sz.height/scalar}};

UIGraphicsBeginImageContextWithOptions(v.bounds.size, YES, 0);
[v drawViewHierarchyInRect:v.bounds afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[innerView removeFromSuperview];

UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[v addSubview:imageView];

The new code just moves the bottom half to the completion block of UIView animateWithDuration:completion. Notice that the duration of the UIView animation is 0, but that still seems to give that extra refresh cycle to update the screen with the new UI.

UIViewController *vlController = [[ComplicatedViewController alloc]init];
UIView *innerView = vlController.view;
[UIView animateWithDuration:0.0 animations:^{
  [v addSubview:innerView];
  innerView.transform = CGAffineTransformMakeScale(scalar, scalar);
  innerView.center = CGRectCenterPoint(v.bounds);
  auto sz = v.bounds.size;
  innerView.bounds = {{0,0}, {sz.width/scalar, sz.height/scalar}};
} completion:^(BOOL finished) {      
    UIGraphicsBeginImageContextWithOptions(v.bounds.size, YES, 0);
    BOOL drawResult = [v drawViewHierarchyInRect:v.bounds afterScreenUpdates:NO];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    [innerView removeFromSuperview];

    [self add:image forFile:v.filename withOrientation:v.orientation];
    UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
    [v addSubview:imageView];
}];
like image 35
gcs Avatar answered Nov 16 '22 06:11

gcs