I am so confused with how to do what I need! Any help is very appreciated! I have an transparent image overlay that has another image behind it that has a UIPanGestureRecognizer and a UIPinchGestureRecognizer attached to it. The top image has a completely transparent middle that serves as the "glass" view. I am trying to crop the bottom image to the "glass" part based off pan and pinch. (see the image below to see what i'm talking about) I have successfully taken care of the pan crop but am having issues cropping it correctly when pinch is also applied.
I am not using snapshotViewAfterScreenUpdates.
Here is the code I have so far:
UIImage *snapshotImage;
/* draw the image of all the views below our view */
UIGraphicsBeginImageContextWithOptions(self.pendantImageView.bounds.size, NO, 0);
BOOL successfulDrawHierarchy = [self.pendantImageView drawViewHierarchyInRect:self.pendantImageView.bounds afterScreenUpdates:YES];
if ( successfulDrawHierarchy ) {
snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
} else {
NSLog(@"drawViewHierarchyInRect:afterScreenUpdates: failed - there's nothing to draw...");
}
UIGraphicsEndImageContext();
UIImage *croppedImage;
if ( successfulDrawHierarchy ) {
/* calculate the coordinates of the rectangle we're interested in within the returned image */
CGRect cropRect = CGRectOffset(pendantFrame, - self.pendantImageView.frame.origin.x, - self.pendantImageView.frame.origin.y);
/* draw the cropped section with a clipping region */
UIGraphicsBeginImageContextWithOptions(cropRect.size, YES, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClipToRect(context, CGRectMake(0, 0, cropRect.size.width, cropRect.size.height));
CGRect targetRectangeForCrop = CGRectMake(-cropRect.origin.x, -cropRect.origin.y, snapshotImage.size.width, snapshotImage.size.height);
[snapshotImage drawInRect:targetRectangeForCrop];
croppedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
pendantImageView is the bottom imageView and pendantFrame is the middle coords of the area i'm trying to crop to.
Thanks in advance!
I am not sure whether I understood you correctly, but here is my result:
(click on the video to watch full version)
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
UIImageView *theImageView = [UIImageView new];
[self.view addSubview:theImageView];
theImageView.image = [UIImage imageNamed:@"image1.jpg"];
theImageView.frame = self.view.frame;
theImageView.userInteractionEnabled = YES;
theImageView.alpha = 0.6;
UIImageView *thePanImageView = [UIImageView new];
[self.view addSubview:thePanImageView];
thePanImageView.frame = CGRectMake(0, 0, 100, 100);
thePanImageView.center = CGPointMake(theImageView.frame.size.width/2, theImageView.frame.size.height/2);
thePanImageView.image = [self screenshotFromRect:thePanImageView.frame fromView:theImageView];
thePanImageView.userInteractionEnabled = YES;
thePanImageView.layer.cornerRadius = thePanImageView.frame.size.width/2;
thePanImageView.clipsToBounds = YES;
thePanImageView.theWeakObject = theImageView;
{
UIPanGestureRecognizer *thePanGesture = [UIPanGestureRecognizer new];
[thePanGesture addTarget:self action:@selector(handlePanGesture:)];
[thePanImageView addGestureRecognizer:thePanGesture];
UIPinchGestureRecognizer *thePinchGesture = [UIPinchGestureRecognizer new];
[thePinchGesture addTarget:self action:@selector(handlePinchGesture:)];
[thePanImageView addGestureRecognizer:thePinchGesture];
}
}
- (void)handlePanGesture:(UIPanGestureRecognizer *)thePanGesture
{
UIImageView *thePanImageView = (id)thePanGesture.view;
UIImageView *theImageView = thePanImageView.theWeakObject;
thePanImageView.center = [thePanGesture locationInView:theImageView];
thePanImageView.image = [self screenshotFromRect:thePanImageView.frame fromView:theImageView];
}
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)thePinchGesture
{
UIImageView *thePanImageView = (id)thePinchGesture.view;
static CGRect theInitialFrame;
if (thePinchGesture.state == UIGestureRecognizerStateBegan)
{
theInitialFrame = thePanImageView.frame;
}
else
{
CGRect theFrame = theInitialFrame;
theFrame.size.width *= thePinchGesture.scale;
theFrame.size.height *= thePinchGesture.scale;
thePanImageView.frame = theFrame;
}
thePanImageView.layer.cornerRadius = thePanImageView.frame.size.width/2;
UIImageView *theImageView = thePanImageView.theWeakObject;
thePanImageView.center = [thePinchGesture locationInView:theImageView];
thePanImageView.image = [self screenshotFromRect:thePanImageView.frame fromView:theImageView];
}
- (UIImage * __nonnull)screenshotFromRect:(CGRect)theRect fromView:(UIView * __nonnull)theView;
{
if (!theView)
{
abort();
}
if (theRect.size.height < 1 || theRect.size.width < 1)
{
abort();
}
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)])
{
UIGraphicsBeginImageContextWithOptions(theRect.size, YES, [UIScreen mainScreen].scale);
}
else
{
UIGraphicsBeginImageContext(theRect.size);
}
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(ctx, -theRect.origin.x, -theRect.origin.y);
[theView.layer renderInContext:ctx];
UIImage *snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return snapshotImage;
}
The above solution is inefficient in terms of CPU, because it takes screenshots every time you move the view.
A much more efficient way would be to create an extra UIImageView
, and simply move it inside of your thePanImageView
The code is the following:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
UIImageView *theImageView = [UIImageView new];
[self.view addSubview:theImageView];
theImageView.image = [UIImage imageNamed:@"image1.jpg"];
theImageView.frame = self.view.frame;
theImageView.userInteractionEnabled = YES;
theImageView.alpha = 0.6;
UIImageView *thePanImageView = [UIImageView new];
[self.view addSubview:thePanImageView];
thePanImageView.frame = CGRectMake(0, 0, 100, 100);
thePanImageView.center = CGPointMake(theImageView.frame.size.width/2, theImageView.frame.size.height/2);
thePanImageView.userInteractionEnabled = YES;
thePanImageView.layer.cornerRadius = thePanImageView.frame.size.width/2;
thePanImageView.clipsToBounds = YES;
thePanImageView.layer.borderColor = [UIColor redColor].CGColor;
thePanImageView.layer.borderWidth = 1;
thePanImageView.theWeakObject = theImageView;
{
UIPanGestureRecognizer *thePanGesture = [UIPanGestureRecognizer new];
[thePanGesture addTarget:self action:@selector(handlePanGesture:)];
[thePanImageView addGestureRecognizer:thePanGesture];
UIPinchGestureRecognizer *thePinchGesture = [UIPinchGestureRecognizer new];
[thePinchGesture addTarget:self action:@selector(handlePinchGesture:)];
[thePanImageView addGestureRecognizer:thePinchGesture];
UIImageView *theExtraImageView = [UIImageView new];
[thePanImageView addSubview:theExtraImageView];
theExtraImageView.frame = CGRectMake(-thePanImageView.frame.origin.x, -thePanImageView.frame.origin.y, theImageView.frame.size.width, theImageView.frame.size.height);
theExtraImageView.image = theImageView.image;
}
}
- (void)handlePanGesture:(UIPanGestureRecognizer *)thePanGesture
{
UIImageView *thePanImageView = (id)thePanGesture.view;
UIImageView *theImageView = thePanImageView.theWeakObject;
thePanImageView.center = [thePanGesture locationInView:theImageView];
UIImageView *theExtraImageView = thePanImageView.subviews.firstObject;
theExtraImageView.frame = CGRectMake(-thePanImageView.frame.origin.x, -thePanImageView.frame.origin.y, theExtraImageView.frame.size.width, theExtraImageView.frame.size.height);
}
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)thePinchGesture
{
UIImageView *thePanImageView = (id)thePinchGesture.view;
static CGRect theInitialFrame;
if (thePinchGesture.state == UIGestureRecognizerStateBegan)
{
theInitialFrame = thePanImageView.frame;
}
else
{
CGRect theFrame = theInitialFrame;
theFrame.size.width *= thePinchGesture.scale;
theFrame.size.height *= thePinchGesture.scale;
thePanImageView.frame = theFrame;
}
thePanImageView.layer.cornerRadius = thePanImageView.frame.size.width/2;
UIImageView *theImageView = thePanImageView.theWeakObject;
thePanImageView.center = [thePinchGesture locationInView:theImageView];
UIImageView *theExtraImageView = thePanImageView.subviews.firstObject;
theExtraImageView.frame = CGRectMake(-thePanImageView.frame.origin.x, -thePanImageView.frame.origin.y, theExtraImageView.frame.size.width, theExtraImageView.frame.size.height);
}
FYI:
theWeakObject
is just my custom property to NSObject
. I used it because I was lazy and didn't want to create globally-visible @property
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With