Search code examples
objective-ciosuikitcore-animation

renderInContext does not capture rotated subviews


A UIImageVIew (imageView) is added to self.view, capturing the view to album works perfectly:

 CGSize size = CGSizeMake(self.view.frame.size.height, self.view.frame.size.width);
 UIGraphicsBeginImageContext(size);
 [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
 UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
 UIGraphicsEndImageContext();
 UIImageWriteToSavedPhotosAlbum(image, self, nil, nil);

However, if the imageView subview is rotated by:

 [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];

Capturing self.view again does not reflected the rotation. The subview imageView is as if not rotated.

How to make renderInContext works with subviews rotated by CABasicAnimation?

UPDATE:

Warning: The CALayer/-renderInContext: method doesn't implement the full Core Animation composition model. The code provided below will be able to resolve most of the situations, but there are things that the CALayer/-renderInContext: method doesn't render correctly, so you may wish to contact Developer Technical Support for workaround requests.

Official Apple Technical Q&A QA1703 suggested to contact Developer Technical Support for workaround requests.

Is there a workaround already existed?


Solution

  • Use layer.presentationLayer instead of layer when renderInContext.

    Here is a category for taking screenshots of UIView, UIView+Screenshot.h:

     //
     //  UIView+Screenshot.h
     //
     //  Created by Horace Ho on 2012/12/11.
     //  Copyright (c) 2012 Horace Ho. All rights reserved.
     //
     // Permission is hereby granted, free of charge, to any person obtaining a copy
     // of this software and associated documentation files (the "Software"), to deal
     // in the Software without restriction, including without limitation the rights
     // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     // copies of the Software, and to permit persons to whom the Software is
     // furnished to do so, subject to the following conditions:
     //
     // The above copyright notice and this permission notice shall be included in
     // all copies or substantial portions of the Software.
     //
     // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     // THE SOFTWARE.
    
     @interface UIView (HHScreenShot)
     - (UIImage *)screenshot:(UIDeviceOrientation)orientation isOpaque:(BOOL)isOpaque usePresentationLayer:(BOOL)usePresentationLayer;
     @end
    
     @implementation UIView (HHScreenShot)
    
     - (UIImage *)screenshot:(UIDeviceOrientation)orientation isOpaque:(BOOL)isOpaque usePresentationLayer:(BOOL)usePresentationLayer
     {
         CGSize size;
    
         if (orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationPortraitUpsideDown) {
             size = CGSizeMake(self.frame.size.width, self.frame.size.height);
         } else {
             size = CGSizeMake(self.frame.size.height, self.frame.size.width);
         }
    
         UIGraphicsBeginImageContextWithOptions(size, isOpaque, 0.0);
    
         if (usePresentationLayer) {
             [self.layer.presentationLayer renderInContext:UIGraphicsGetCurrentContext()];
         } else {
             [self.layer renderInContext:UIGraphicsGetCurrentContext()];
         }
    
         UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    
         UIGraphicsEndImageContext();
    
         return image;
     }
    
     @end
    

    To use:

     UIImage *image = [self.view screenshot:UIDeviceOrientationPortrait
                                   isOpaque:YES 
                       usePresentationLayer:YES];