Scale image based off UIPinch
Asked Answered
C

2

13

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!

crop image to the area in the middle

Contented answered 26/1, 2016 at 3:30 Comment(2)
hi, did my answer help you at all?Evert
@Evert Hi! Sorry I ment to respond back more promptly. This is not what I was asking. I guess my question wasn't as clear as I thought it was. I will be revising it. Thanks for being the only one to try and help.Contented
E
3

I am not sure whether I understood you correctly, but here is my result:

(click on the video to watch full version)

Demo CountPages alpha

- (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;
}

EDIT:

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

Evert answered 30/1, 2016 at 20:2 Comment(0)
D
0

you should ask for how to output a image use mask! here is the link for the solution:

CGContextClipToMask returning blank image

you need to change:

CGContextClipToRect(context, CGRectMake(0, 0, cropRect.size.width, cropRect.size.height));

to something like:

CGContextClipToMask(context, CGRectMake(0, 0, cropRect.size.width, cropRect.size.height), maskImage); 
Doleful answered 3/2, 2016 at 22:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.