iOS8 scale glitch when calling drawViewHierarchyIn

2019-03-11 03:26发布

问题:

I was converting a project from iOS7 to iOS8 which uses custom transitions and needs to capture the modal after it finishes loading afterScreenUpdates:YES and was seeing that the entire screen scale up for a second and scale back down. I also see this happening in the Flickr app for iOS between sections and on Yelp app when transitioning to a photo on iOS8.

  UIGraphicsBeginImageContextWithOptions(self.view.frame.size, YES, 22.0); 
  [self.view drawViewHierarchyInRect:self.view.frame afterScreenUpdates:YES];
  UIGraphicsEndImageContext();

Adding a larger scale factor helps emphasize the glitch more... but i'm just calling this on a button press in the example.

EDIT This appears to happen on iPhone 6 and 6 plus not on the 5.

Sample project github

回答1:

Do you NEED it to draw after the screen updates? because I'm using:

[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO];

and it seems to work fine on iOS7 and iOS8. I imagine this isn't a great solution for capturing images regularly (like multiple times a second) but it seems to work for a once off blur.



回答2:

You have to provide @3x launch images for the 6 and 6 plus. The 6 being scaled at 750x1334, and the 6 plus image being scaled at 1242x2208.



回答3:

Even it looks like bug in API, you can call drawViewHierarchyInRect with afterScreenUpdates set to NO to build snapshot AFTER screen updates if you use cunstruction like this:

typedef void (^CompletionHandlerWithId)(id result);

-(void)imageContaining:(CGRect)rect afterScreenUpdates:(bool)afterScreenUpdates opaque:(BOOL)opaque completion:(CompletionHandlerWithId)completion
{
    bool success __block;
    UIImage *snapshotImage __block = nil;

    CompletionHandler block = ^{

        // Create the image
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, opaque, [[UIScreen mainScreen] scale]);

        success = [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:NO];

        if (success)
        {
            snapshotImage = UIGraphicsGetImageFromCurrentImageContext();

            CGImageRef imageRef = CGImageCreateWithImageInRect(
                                   [snapshotImage CGImage],
                                   CGRectMake(
                                          snapshotImage.scale*rect.origin.x,
                                          snapshotImage.scale*rect.origin.y,
                                          snapshotImage.scale*rect.size.width,
                                          snapshotImage.scale*rect.size.height));

            // or use the UIImage wherever you like
            snapshotImage = [UIImage imageWithCGImage:imageRef scale:snapshotImage.scale orientation:UIImageOrientationUp];

            CGImageRelease(imageRef);
        }

        UIGraphicsEndImageContext();

        if (completion)
        {
            if (! success)
            {
                NSLog(@"Error: [UIView drawViewHierarchyInRect] failed on %@", self);

                (completion)(nil);
            }
            else
            {
                NSLog(@"Success: [UIView drawViewHierarchyInRect] on %@", self);

                (completion)(snapshotImage);
            }
        }
    };

    if (afterScreenUpdates)

        [CATransaction setCompletionBlock:^{
            (block)();
        }];

    else

        (block)();
}    


回答4:

This bug also exists when you run on an iPad2 running iOS7.

Fix: set afterScreenUpdates: to NO

My app has some moving UIButtons, so I don't allow the blur transition until after the movement has stopped. As far as I have found so far, there is no difference in YES or NO.



回答5:

Appears to be fixed in iOS9 / XCODE 7 builds



回答6:

I found a solution for me to solve this problem.

I add @3x launch images to my project. And choose launch Screen file to "Main". This will make app run at original resolution, smaller bounds when run at iphone6, but not glitch when calling drawViewHierarchyInRect. Like this.

Then, scale my view to fullscreen when viewDidLoad.

- (void)viewDidLoad{ 
       [super viewDidLoad];
       UIScreen *mainScreen = [UIScreen mainScreen];
       CGRect tempFrame=mainScreen.bounds;
       double aspect=tempFrame.size.width/320;
       self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, aspect, aspect);
}

Hope helpful :)