Note that there are some explanatory texts on larger screens.

plurals
  1. POCreate CGpath from UIimageview with rotation and scale for use as cglayer mask
    text
    copied!<p>I am still pretty new to iOS, though I can't figure out why this doesn't work.</p> <p>I have 2 separate UIImageView's</p> <ol> <li>The 'Background'. This is a picture that is going to have a layer mask applied to it so that you can only see part of the image.</li> <li>The 'Mask Image' this is it's own independent image that can be rotated, panned, pinch zoomed. The code for rotate/scale works fine and is standard code.</li> </ol> <p>What I am trying to do is create a CGPath that matches the current location/rotation/scale of the 'Mask Image'</p> <p>Here is my code for grabbing the location of the 'Mask Image' and creating a CGPAth to use as a layer mask.</p> <p>If you go to Xcode and make a new 'Single View Application' project, here is the full copy paste of the ViewController</p> <p>ViewController.h:</p> <pre><code>#import &lt;UIKit/UIKit.h&gt; @interface ViewController : UIViewController &lt;UIGestureRecognizerDelegate&gt; { UIImageView *makskedImageView; UIImageView *maskImageView; } @end </code></pre> <p>ViewController.m</p> <pre><code>#import "ViewController.h" #import &lt;QuartzCore/QuartzCore.h&gt; @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. UIImage *sourceImage = [UIImage imageNamed:@"forest.jpg"]; UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, (sourceImage.size.width/2), (sourceImage.size.height/2))]; imageView.image = sourceImage; [self.view addSubview:imageView]; makskedImageView = imageView; // Movable Mask UIImage *frameBorder = [UIImage imageNamed:@"semiTransSquare.png"]; maskImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, (frameBorder.size.width/2), (frameBorder.size.height/2))]; maskImageView.image = frameBorder; UIPanGestureRecognizer *panGesture2 = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; [panGesture2 setDelegate:self]; [maskImageView addGestureRecognizer:panGesture2]; UIPinchGestureRecognizer *pinchGesture2 = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)]; [pinchGesture2 setDelegate:self]; [maskImageView addGestureRecognizer:pinchGesture2]; UIRotationGestureRecognizer *rotateGesture2 = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(handleRotate:)]; [rotateGesture2 setDelegate:self]; [maskImageView addGestureRecognizer:rotateGesture2]; UITapGestureRecognizer *tapGR =[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleTap:)]; [tapGR setDelegate:self]; [tapGR setNumberOfTapsRequired:1]; [maskImageView addGestureRecognizer:tapGR]; [maskImageView setUserInteractionEnabled:YES]; // Add to view to make visible; [self.view addSubview:maskImageView]; // apply layer mask to makskedImageView [self grabTransform:maskImageView]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)grabTransform:(UIView *)currentView { // build the transform you want CGAffineTransform t = CGAffineTransformIdentity; CGFloat angle = [(NSNumber *)[currentView valueForKeyPath:@"layer.transform.rotation.z"] floatValue]; CGFloat scale = [(NSNumber *)[currentView valueForKeyPath:@"layer.transform.scale"] floatValue]; t = CGAffineTransformConcat(t, CGAffineTransformMakeScale(scale, scale)); t = CGAffineTransformConcat(t, CGAffineTransformMakeRotation(angle)); NSLog(@"angle: %f, scale: %f", angle, scale); // Create a mask layer // Create a mask layer and the frame to determine what will be visible in the view. CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init]; // CGRect maskRect = currentView.bounds; // Frame works a lot better. CGRect maskRect = currentView.frame; maskRect.origin.x = currentView.frame.origin.x; maskRect.origin.y = currentView.frame.origin.y; // Make a Path friendly transform. CGPoint center = CGPointMake(CGRectGetMidX(currentView.frame), CGRectGetMidY(currentView.frame)); CGAffineTransform transform = CGAffineTransformIdentity; transform = CGAffineTransformTranslate(transform, center.x, center.y); transform = CGAffineTransformScale(transform, scale, scale); transform = CGAffineTransformRotate(transform, angle); transform = CGAffineTransformTranslate(transform, -center.x, -center.y); // Create a path and from the rectangle in it. CGPathRef path = CGPathCreateWithRect(maskRect, &amp;transform); // Set the path to the mask layer. [maskLayer setPath:path]; // Release the path since it's not covered by ARC. CGPathRelease(path); // Set the mask of the view. makskedImageView.layer.mask = maskLayer; } #pragma mark - Gestures -(void)handlePan:(UIPanGestureRecognizer *)recognizer { CGPoint translation = [recognizer translationInView:self.view]; recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, recognizer.view.center.y + translation.y); [recognizer setTranslation:CGPointMake(0, 0) inView:self.view]; [self grabTransform:[recognizer view]]; } -(void)handlePinch:(UIPinchGestureRecognizer *)recognizer { recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale); recognizer.scale = 1; [self grabTransform:[recognizer view]]; } -(void)handleRotate:(UIRotationGestureRecognizer *)recognizer { recognizer.view.transform = CGAffineTransformRotate(recognizer.view.transform, recognizer.rotation); recognizer.rotation = 0; valueForKeyPath:@"layer.transform.rotation"] floatValue]; [self grabTransform:[recognizer view]]; } -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return YES; } - (void)handleTap:(UIGestureRecognizer *)recognizer { [self grabTransform:[recognizer view]]; } @end </code></pre> <p>Plus 2 Images: <a href="http://i.stack.imgur.com/H5b1P.jpg" rel="nofollow">Forest.jpg</a> &amp; <a href="http://i.stack.imgur.com/Vgcus.png" rel="nofollow">semiTransSquare.png</a></p> <p>Now when this runs it works for panning until I scale.rotate the image. (rotate working now thanks to some other code).</p> <p>From what I can tell scale works with the Center as the point around which it grows/shrinks, where the path seems to scale out from the root x,y co-ordinates. </p> <p>This might be by design (<a href="http://developer.apple.com/library/ios/#documentation/GraphicsImaging/Reference/CGPath/Reference/reference.html#//apple_ref/c/func/CGPathCreateWithRect" rel="nofollow">CGPathCreateWithRect per the docs</a>) but I can't see a way to apply the transforms to the CGPath and have it appear in the same place as the UIImageView 'Mask Image'</p> <p>Performance is a big issue and a similar solution with CIImage was very laggy so would like to solve this in the context of having CGPath create the layer mask if that is possible, or something that is just as fast.</p> <p>What am I missing? Happy for iOS 6 only solutions.</p> <p><strong>Update:</strong> I found that changing from bounds to frame gets me 90% of the way there:</p> <pre><code>CGRect maskRect = currentView.frame; </code></pre> <p>The path will now match the rotation and position of the view. The only issue remaining is that the scale is wrong. It matches at the original size, but if you shrink then it gets too small or if you grow it gets too big. Looks like the scale isn't 1:1. So close now.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload