Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scale image based off UIPinch

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

like image 858
gikygik Avatar asked Jan 26 '16 03:01

gikygik


1 Answers

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

like image 164
OlDor Avatar answered Oct 12 '22 08:10

OlDor