Mask problem with renderInContext of CALayer
Opened this issue · 4 comments
When using renderInContext method of CALayer the mask layers are not rendered. The result looks ugly, the entire track image is show in the UIImage.
I know the real problem is that renderInContext does not support mask layer -- but is there something that can be done in TTSwitch to address this? My app requires screenshot of UIView which contains the Switch. I just finished replacing all the Swtiches in the app with TTSwitch... would hate to revert back to UISwitch.
Test code to reproduce the issue (modified the example project):
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMailComposeViewController.h>
@interface TTXibViewController : UIViewController <MFMailComposeViewControllerDelegate>
@end
#import "TTXibViewController.h"
#import "TTSwitch.h"
#import <QuartzCore/QuartzCore.h>
@interface TTXibViewController ()
@property (weak, nonatomic) IBOutlet TTSwitch *defaultSwitch;
@end
@implementation TTXibViewController
- (IBAction)sendEmail:(id)sender {
UIGraphicsBeginImageContextWithOptions(self.view.frame.size, NO, 0.0);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData * data = UIImagePNGRepresentation(screenshot);
[self emailImageWithImageData:data];
}
- (void)emailImageWithImageData:(NSData *)data
{
MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc] init];
mailController.mailComposeDelegate = self;
[mailController setSubject:@"Test Email"];
[mailController setMessageBody:nil isHTML:NO];
[mailController addAttachmentData:data mimeType:@"image/png" fileName:@"test.png"];
[self.navigationController presentModalViewController:mailController animated:YES];
}
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
[self dismissModalViewControllerAnimated:YES];
}
@end
;
I am not sure how to solve this problem without rewriting the switch to draw with core graphics. I am open to any ideas on how to change the way the masking currently works to fix it.
I wasted two days, but finally i had to create my own logic.
I have solved this problem by a tricky logic.
I created following code to get UIImage from UIView.subviews.
Actually i am having Subviews. And my subView's layer is masked by CAShapeLayer.
modify this code according to your requirement.
-(UIImage*) imageFromView:(UIView*)originalView
{
//Creating a fakeView which is initialized as our original view.
//Actually i render images on a fakeView and take screenshot of this fakeView.
UIView *fakeView = [[UIView alloc] initWithFrame:originalView.frame];
[fakeView setBackgroundColor:originalView.backgroundColor];
[fakeView.layer setMasksToBounds:originalView.layer.masksToBounds];
[fakeView.layer setCornerRadius:originalView.layer.cornerRadius];
[fakeView.layer setBorderColor:originalView.layer.borderColor];
[fakeView.layer setBorderWidth:originalView.layer.borderWidth];
//Getting subviews of originalView.
for (UIView *view in originalView.subviews)
{
//Getting screenshot of layer of view.
UIGraphicsBeginImageContext(view.bounds.size);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *imageLayer = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//If it is masked view, then get masked image.
if (view.layer.mask)
{
//getting screenshot of masked layer.
UIGraphicsBeginImageContext(view.bounds.size);
[view.layer.mask renderInContext:UIGraphicsGetCurrentContext()];
//PNG Representation is most important. otherwise this masked image will not work.
UIImage *maskedLayerImage=[UIImage imageWithData:UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext())];
UIGraphicsEndImageContext();
//getting image by masking original image.
imageLayer = [self maskImage:imageLayer withMask:maskedLayerImage];
}
//If imageLayer is pointing to a valid object, then setting this image to UIImageView, our UIImageView frame having exactly as view.frame.
if (imageLayer)
{
UIImageView *imageView = [[UIImageView alloc] initWithFrame:view.frame];
[imageView setImage:imageLayer];
[fakeView addSubview:imageView];
}
}
//At the end, taking screenshot of fakeView. This will get your Original Image.
UIGraphicsBeginImageContext(fakeView.bounds.size);
[fakeView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *previewCapturedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return previewCapturedImage;
}
}
//Method is used to get masked image.
- (UIImage*) maskImage:(UIImage *)image withMask:(UIImage *)maskImage
{
CGImageRef maskRef = maskImage.CGImage;
CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef), NULL, false);
CGImageRef masked = CGImageCreateWithMask([image CGImage], mask);
return [UIImage imageWithCGImage:masked];
}
Thanks hackiftekhar! Your tricky solution worked. Just had to modify a little according to my view setup.
most welcome.