How to crop image inside the circle in UIImageView in iOS
Asked Answered
A

4

27

I have an app where I have a UIImageView which displays main image and another UIImageView being used as a mask which shows a circle which is transparent and outside its opaque, this circle can be moved using a UIPanGestureRecognizer, I want to know a wayout to crop the image inside the circle into a new image. Here is the attached code and screen shot

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    // create pan gesture

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self
                                                                          action:@selector(handlePan:)];
    [self.view addGestureRecognizer:pan];


    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.path = [[self makeCircleAtLocation:self.view.center radius:100.0] CGPath];
    shapeLayer.strokeColor = [[UIColor clearColor] CGColor];
    shapeLayer.fillColor = nil;
    shapeLayer.lineWidth = 3.0;

    // Add CAShapeLayer to our view

    [self.view.layer addSublayer:shapeLayer];

    // Save this shape layer in a class property for future reference,
    // namely so we can remove it later if we tap elsewhere on the screen.

    self.circleLayer = shapeLayer;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
// Create a UIBezierPath which is a circle at a certain location of a certain radius.
// This also saves the circle's center and radius to class properties for future reference.

- (UIBezierPath *)makeCircleAtLocation:(CGPoint)location radius:(CGFloat)radius
{
    self.circleCenter = location;
    self.circleRadius = radius;

    UIBezierPath *path = [UIBezierPath bezierPath];
    [path addArcWithCenter:self.circleCenter
                    radius:self.circleRadius
                startAngle:0.0
                  endAngle:M_PI * 2.0
                 clockwise:YES];

    return path;
}

- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
    static CGPoint oldCenter;

    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        // If we're starting a pan, make sure we're inside the circle.
        // So, calculate the distance between the circle's center and
        // the gesture start location and we'll compare that to the
        // radius of the circle.

        CGPoint location = [gesture locationInView:gesture.view];
        CGPoint translation = [gesture translationInView:gesture.view];
        location.x -= translation.x;
        location.y -= translation.y;

        CGFloat x = location.x - self.circleCenter.x;
        CGFloat y = location.y - self.circleCenter.y;
        CGFloat distance = sqrtf(x*x + y*y);

        // If we're outside the circle, cancel the gesture.
        // If we're inside it, keep track of where the circle was.


        oldCenter = self.circleCenter;
    }
    else if (gesture.state == UIGestureRecognizerStateChanged)
    {
        // Let's calculate the new center of the circle by adding the
        // the translationInView to the old circle center.

        CGPoint translation = [gesture translationInView:gesture.view];
        CGPoint newCenter = CGPointMake(oldCenter.x + translation.x, oldCenter.y + translation.y);

      //  CGPoint newCenter = [gesture locationInView:self.view];
        if (newCenter.x < 160) {
            newCenter.x = 160;
        }
        else if (newCenter.x > self.view.frame.size.width - 160) {
            newCenter.x = self.view.frame.size.width - 160;
        }
        if (newCenter.y < 242) {
            newCenter.y = 242;
        }
        else if (newCenter.y > self.view.frame.size.height - imageMain.center.y) {
            newCenter.y = self.view.frame.size.height - imageMain.center.y;
        }

        // Update the path for our CAShapeLayer

       // self.circleLayer.path = [[self makeCircleAtLocation:newCenter radius:self.circleRadius] CGPath];
        imageCircle.center = newCenter;

    }
}

@end

And the result is

enter image description here

Aho answered 23/11, 2013 at 18:3 Comment(1)
Do you want the same image as output that you are seeing on screen at that moment? If you want that, you can create a new graphics context and after that just render the base image and the content of your CAShapeLayer, in turn, to that context and you are gonna get the desired output. Have you tried this method?Eridanus
H
68

To save the masked image, one would use drawHierarchy(in:afterScreenUpdates:). You might also want to crop the image with cropping(to:). See my handleTap below for an example.

But I note that you are apparently masking by overlaying an image. I might suggest using a UIBezierPath for the basis of both a layer mask for the image view, as well as the CAShapeLayer you'll use to draw the circle (assuming you want a border as you draw the circle. If your mask is a regular shape (such as a circle), it's probably more flexible to make it a CAShapeLayer with a UIBezierPath (rather than an image), because that way you can not only move it around with a pan gesture, but also scale it, too, with a pinch gesture:

enter image description here

Here is a sample implementation:

//  ViewController.swift
//  CircleMaskDemo
//
//  Created by Robert Ryan on 4/18/18.
//  Copyright © 2018-2022 Robert Ryan. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var pinch: UIPinchGestureRecognizer!
    @IBOutlet weak var pan: UIPanGestureRecognizer!

    private let maskLayer = CAShapeLayer()

    private lazy var radius: CGFloat = min(view.bounds.width, view.bounds.height) * 0.3
    private lazy var center: CGPoint = CGPoint(x: view.bounds.midX, y: view.bounds.midY)

    private let pathLayer: CAShapeLayer = {
        let _pathLayer = CAShapeLayer()
        _pathLayer.fillColor = UIColor.clear.cgColor
        _pathLayer.strokeColor = UIColor.black.cgColor
        _pathLayer.lineWidth = 3
        return _pathLayer
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        imageView.layer.addSublayer(pathLayer)
        imageView.layer.mask = maskLayer
        imageView.isUserInteractionEnabled = true
        imageView.addGestureRecognizer(pinch)
        imageView.addGestureRecognizer(pan)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        updateCirclePath(at: center, radius: radius)
    }

    private var oldCenter: CGPoint!
    private var oldRadius: CGFloat!
}

// MARK: - Actions

extension ViewController {
    @IBAction func handlePinch(_ gesture: UIPinchGestureRecognizer) {
        let scale = gesture.scale

        if gesture.state == .began { oldRadius = radius }

        updateCirclePath(at: center, radius: oldRadius * scale)
    }

    @IBAction func handlePan(_ gesture: UIPanGestureRecognizer) {
        let translation = gesture.translation(in: gesture.view)

        if gesture.state == .began { oldCenter = center }

        let newCenter = CGPoint(x: oldCenter.x + translation.x, y: oldCenter.y + translation.y)

        updateCirclePath(at: newCenter, radius: radius)
    }

    @IBAction func handleTap(_ gesture: UITapGestureRecognizer) {
        let fileURL = try! FileManager.default
            .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            .appendingPathComponent("image.png")

        let scale  = imageView.window!.screen.scale
        let radius = self.radius * scale
        let center = CGPoint(x: self.center.x * scale, y: self.center.y * scale)

        let frame = CGRect(x: center.x - radius,
                           y: center.y - radius,
                           width: radius * 2.0,
                           height: radius * 2.0)

        // temporarily remove the circleLayer

        let saveLayer = pathLayer
        saveLayer.removeFromSuperlayer()

        // render the clipped image

        let image = UIGraphicsImageRenderer(size: imageView.frame.size).image { _ in
            imageView.drawHierarchy(in: imageView.bounds, afterScreenUpdates: true)
        }

        // add the circleLayer back

        imageView.layer.addSublayer(saveLayer)

        // crop the image

        guard
            let imageRef = image.cgImage?.cropping(to: frame),
            let data = UIImage(cgImage: imageRef).pngData()
        else {
            return
        }

        // save the image

        try? data.write(to: fileURL)

        // tell the user we're done

        let alert = UIAlertController(title: nil, message: "Saved", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }
}

// MARK: - Private utility methods

private extension ViewController {
    func updateCirclePath(at center: CGPoint, radius: CGFloat) {
        self.center = center
        self.radius = radius

        let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: 2 * .pi, clockwise: true)
        maskLayer.path = path.cgPath
        pathLayer.path = path.cgPath
    }
}

// MARK: - UIGestureRecognizerDelegate

extension ViewController: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        let tuple = (gestureRecognizer, otherGestureRecognizer)
        return tuple == (pan, pinch) || tuple == (pinch, pan)
    }
}

If you don't want to draw the border around the circle, then it's even easier, as you can pull anything related to circleLayer.

If you're interested in Objective-C example, see previous revision of this answer.

Humiliate answered 24/11, 2013 at 4:34 Comment(18)
Perfect , you are great man! :) thanks i wanted to give atleast 10 vote ups :)Aho
Hi Rob, thanks for the post, everything works cool but I want background color to be transparent instead of black. What changes should I do. Thanks in advance.Division
@Division I'm just masking it (thus revealing whatever was behind it). In my example, I happen to have a basic white UIView. Put whatever you want behind it, and as you mask the image view, what was behind it will be revealed.Humiliate
@Humiliate hey thanks for this awesome code, but when I perform my save I am seeing a square image not a circle?Genteelism
@Humiliate I was able to solve my problem, but something else crossed my mind, in this code and such, you use an UIImageView with an IBOutlet, lets say I didn't want to display the image I had to the user, but still crop it I could just hide the UIImageView but that seems like a cheat kinda. Would it be possible to do this without displaying the image? I was thinking what if I just made a UIImageView in code but never added it to the view, would that work? Or is there another way? ThanksGenteelism
@Humiliate thanks for the reply I posted new question here if you are interested. #29615456. I would really appreciate the help.Genteelism
@Humiliate How can we change the color of this White portion of mask shown in you .gif image?Backfire
@RajatDeepSingh - The color is just whatever the color of the view behind the image view is. So change that view's color.Humiliate
@Humiliate But behind my CAShapeLayer I am having map & I can't change the color of my mapBackfire
@Humiliate I tried to give the background color of my mapview, but that too dont worked.Backfire
If you can't get the mask of the map view to work, then the logical alternative is to put something in front of the map view and mask everything except the circle (thereby revealing the map view, below it). Or if you don't need a dynamic map, create a snapshot of the map, and then using that resulting image, and then use that image in an image view and then apply the mask technique I outlined in my answer above.Humiliate
@Humiliate I've come across an issue that I'm not sure how to resolve; perhaps you can suggest a solution. Problem: I need to restrict the circle path to the dimensions of the UIImageView that is showing the image I need to crop. The UIImageView is currently not taking the full area of the view controller because I'm limiting it to a specific size within the graphical layout I'm showing in that view. Therefore, the circle path is moving over the UI surrounding the UIImageView, not just the UIImageView. Any ideas? Thanks for sharing your skills.Gaelic
@Gaelic - You're adjusting the center/radius in the gesture handlers, so constrain that any way you want (e.g. if adjusting radius, don't let it exceed the distance to nearest edge; if adjusting center, don't let it be closer to the nearest edge than the current radius). But, again, try it out, and if you can't figure it out, don't post a comment here, but rather post your own question showing us what you tried, describe what happened, and describe what you wanted to happen.Humiliate
Hey Rob, how to crop oval face shape image, can you please suggest.Bazaar
@RamS - The above uses addArcWithCenter to draw a circle. Use bezierPathWithRoundedRect:cornerRadius: instead.Humiliate
how to implement in swift?Aquiculture
Hey @Humiliate great answer! Could you please update it to SwiftUI?Beeler
I've updated the UIKit for contemporary APIs. I'll take a crack at a SwiftUI rendition when I get the chance (which might be a while).Humiliate
L
5

Same code reused to play with Square, it may be helpful for other.

#import "ViewController.h"

@interface ViewController () <UIGestureRecognizerDelegate>

@property (weak, nonatomic) IBOutlet UIImageView *imageView;

@property (nonatomic) CGFloat circleRadius;
@property (nonatomic) CGPoint circleCenter;
@property (nonatomic) CGRect frame;


@property (nonatomic, weak) CAShapeLayer *maskLayer;
@property (nonatomic, weak) CAShapeLayer *circleLayer;

@property (nonatomic, weak) UIPinchGestureRecognizer *pinch;
@property (nonatomic, weak) UIPanGestureRecognizer   *pan;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // create layer mask for the image

    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    self.imageView.layer.mask = maskLayer;
    self.maskLayer = maskLayer;

    // create shape layer for circle we'll draw on top of image (the boundary of the circle)

    CAShapeLayer *circleLayer = [CAShapeLayer layer];
    circleLayer.lineWidth = 3.0;
    circleLayer.fillColor = [[UIColor clearColor] CGColor];
    circleLayer.strokeColor = [[UIColor blackColor] CGColor];
    [self.imageView.layer addSublayer:circleLayer];
    self.circleLayer = circleLayer;

    // create circle path

    [self updateCirclePathAtLocation:CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0) radius:self.view.bounds.size.width * 0.30];

    // create pan gesture

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
    pan.delegate = self;
    [self.imageView addGestureRecognizer:pan];
    self.imageView.userInteractionEnabled = YES;
    self.pan = pan;

    // create pan gesture

    UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
    pinch.delegate = self;
    [self.view addGestureRecognizer:pinch];
    self.pinch = pinch;
}

- (void)updateCirclePathAtLocation:(CGPoint)location radius:(CGFloat)radius
{
    self.circleCenter = location;
    self.circleRadius = radius;
    self.frame =CGRectMake(self.circleCenter.x/2, self.circleCenter.y/2, self.circleRadius, self.circleRadius);
    UIBezierPath *path =     [UIBezierPath bezierPathWithRoundedRect:self.frame cornerRadius:0];
//    [path addArcWithCenter:self.circleCenter
//                    radius:self.circleRadius
//                startAngle:0.0
//                  endAngle:M_PI * 2.0
//                 clockwise:YES];


    self.maskLayer.path = [path CGPath];
    self.circleLayer.path = [path CGPath];
}

- (IBAction)didTouchUpInsideSaveButton:(id)sender
{
    NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    NSString *path = [documentsPath stringByAppendingPathComponent:@"image.png"];

    CGFloat scale  = [[self.imageView.window screen] scale];

    CGRect frame = CGRectMake(self.frame.origin.x *scale,
                              self.frame.origin.y *scale,
                              self.frame.size.width*scale,
                              self.frame.size.width*scale);

    // temporarily remove the circleLayer

    CALayer *circleLayer = self.circleLayer;
    [self.circleLayer removeFromSuperlayer];

    // render the clipped image

    UIGraphicsBeginImageContextWithOptions(self.imageView.frame.size, NO, 0.0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    if ([self.imageView respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)])
    {
        // if iOS 7, just draw it

        [self.imageView drawViewHierarchyInRect:self.imageView.bounds afterScreenUpdates:YES];
    }
    else
    {
        // if pre iOS 7, manually clip it

        CGContextAddRect(context, self.frame);
        CGContextClip(context);
        [self.imageView.layer renderInContext:context];
    }

    // capture the image and close the context

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    // add the circleLayer back

    [self.imageView.layer addSublayer:circleLayer];

    // crop the image
    NSLog(@"circle fram %@",NSStringFromCGRect(frame));
    NSLog(@"self fram %@",NSStringFromCGRect(self.frame));

    CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], frame);
    UIImage *croppedImage = [UIImage imageWithCGImage:imageRef];

    // save the image

    NSData *data = UIImagePNGRepresentation(croppedImage);
    [data writeToFile:path atomically:YES];

    // tell the user we're done

    [[[UIAlertView alloc] initWithTitle:nil message:@"Saved" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil] show];
}


#pragma mark - Gesture recognizers

- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
    static CGPoint oldCenter;
    CGPoint tranlation = [gesture translationInView:gesture.view];

    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        oldCenter = self.circleCenter;
    }

    CGPoint newCenter = CGPointMake(oldCenter.x + tranlation.x, oldCenter.y + tranlation.y);

    [self updateCirclePathAtLocation:newCenter radius:self.circleRadius];
}

- (void)handlePinch:(UIPinchGestureRecognizer *)gesture
{
    static CGFloat oldRadius;
    CGFloat scale = [gesture scale];

    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        oldRadius = self.circleRadius;
    }

    CGFloat newRadius = oldRadius * scale;

    [self updateCirclePathAtLocation:self.circleCenter radius:newRadius];
}


#pragma mark - UIGestureRecognizerDelegate

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    if ((gestureRecognizer == self.pan   && otherGestureRecognizer == self.pinch) ||
        (gestureRecognizer == self.pinch && otherGestureRecognizer == self.pan))
    {
        return YES;
    }

    return NO;
}

@end
Licence answered 27/3, 2015 at 12:27 Comment(3)
how to other custom crop like face relate shape in this use crop image objective cEagre
use face as transparent overlayLicence
overlay also move and scale ..posibleEagre
E
3
#import "ViewController.h"

@interface ViewController ()


 @property (weak, nonatomic) IBOutlet UIImageView *imageView;
 @property (weak, nonatomic) IBOutlet UIImageView *crppedImageView;


 @property (nonatomic) CGFloat circleRadius;
 @property (nonatomic) CGPoint circleCenter;

 @property (nonatomic, weak) CAShapeLayer *maskLayer;
 @property (nonatomic, weak) CAShapeLayer *maskSubLayer;
 @property (nonatomic, weak) CAShapeLayer *circleLayer;

 @property (nonatomic, weak) UIPinchGestureRecognizer *pinch;
 @property (nonatomic, weak) UIPanGestureRecognizer   *pan;


@end

@implementation ViewController


 - (void)viewDidLoad
{
 [super viewDidLoad];

// create layer mask for the image

CAShapeLayer *maskLayer = [CAShapeLayer layer];
CGRect maskRect = self.imageView.frame;

// Create a path with the rectangle in it.
CGPathRef path = CGPathCreateWithRect(maskRect, NULL);

      // Set the path to the mask layer.
       maskLayer.path = path;

   //    maskLayer.fillRule = kCAFillRuleEvenOdd;
  //    maskLayer.fillColor = [UIColor blueColor].CGColor;
  //    maskLayer.opacity = 0.5;

self.imageView.layer.mask = maskLayer;
self.maskLayer = maskLayer;

CAShapeLayer *maskLayer1 = [CAShapeLayer layer];
CGRect maskRect1 = self.imageView.frame;

// Create a path with the rectangle in it.
CGPathRef path1 = CGPathCreateWithRect(maskRect1, NULL);

// Set the path to the mask layer.
maskLayer1.path = path1;

[self.imageView.layer.mask addSublayer:maskLayer1];
self.maskSubLayer = maskLayer1;


// create shape layer for circle we'll draw on top of image (the boundary of the circle)

CAShapeLayer *circleLayer = [CAShapeLayer layer];
circleLayer.lineWidth = 3.0;
circleLayer.fillColor = [[UIColor blueColor] CGColor];
circleLayer.fillRule = kCAFillRuleEvenOdd;
circleLayer.opacity = 0.5;
circleLayer.strokeColor = [[UIColor blackColor] CGColor];
[self.imageView.layer addSublayer:circleLayer];
self.circleLayer = circleLayer;

// create circle path

[self updateCirclePathAtLocation:CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0) radius:self.view.bounds.size.width * 0.30];

// create pan gesture

UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
pan.delegate = self;
[self.imageView addGestureRecognizer:pan];
self.imageView.userInteractionEnabled = YES;
self.pan = pan;

// create pan gesture

UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
pinch.delegate = self;
[self.view addGestureRecognizer:pinch];
self.pinch = pinch;
 }

    - (void)updateCirclePathAtLocation:(CGPoint)location radius:(CGFloat)radius
  {
self.circleCenter = location;
self.circleRadius = radius;

UIBezierPath *path = [UIBezierPath bezierPath];
[path addArcWithCenter:self.circleCenter
                radius:self.circleRadius
            startAngle:0.0
              endAngle:M_PI * 2.0
             clockwise:YES];

UIBezierPath *path1 = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, self.imageView.bounds.size.width, self.imageView.bounds.size.height) cornerRadius:0];
[path1 appendPath:path];
[path1 setUsesEvenOddFillRule:YES];


self.maskSubLayer.path = [path1 CGPath];
    self.circleLayer.path = [path1 CGPath];

}

    - (IBAction)didTouchUpInsideSaveButton:(id)sender
   {
NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *path = [documentsPath stringByAppendingPathComponent:@"image.png"];

CGFloat scale  = [[self.imageView.window screen] scale];
CGFloat radius = self.circleRadius * scale;
CGPoint center = CGPointMake(self.circleCenter.x * scale, self.circleCenter.y * scale);

CGRect frame = CGRectMake(center.x - radius,
                          center.y - radius,
                          radius * 2.0,
                          radius * 2.0);

// temporarily remove the circleLayer

CALayer *circleLayer = self.circleLayer;
[self.circleLayer removeFromSuperlayer];

// render the clipped image

UIGraphicsBeginImageContextWithOptions(self.imageView.frame.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
if ([self.imageView respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)])
{
    // if iOS 7, just draw it

    [self.imageView drawViewHierarchyInRect:self.imageView.bounds afterScreenUpdates:YES];
}
else
{
    // if pre iOS 7, manually clip it

    CGContextAddArc(context, self.circleCenter.x, self.circleCenter.y, self.circleRadius, 0, M_PI * 2.0, YES);
    CGContextClip(context);
    [self.imageView.layer renderInContext:context];
}

// capture the image and close the context

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

// add the circleLayer back

[self.imageView.layer addSublayer:circleLayer];

// crop the image

CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], frame);
UIImage *croppedImage = [UIImage imageWithCGImage:imageRef];

_crppedImageView.layer.cornerRadius = _crppedImageView.frame.size.height /2;
_crppedImageView.layer.masksToBounds = YES;
_crppedImageView.layer.borderWidth = 0;
self.crppedImageView.image = croppedImage;
// save the image

NSData *data = UIImagePNGRepresentation(croppedImage);
[data writeToFile:path atomically:YES];

// tell the user we're done

[[[UIAlertView alloc] initWithTitle:nil message:@"Saved" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil] show];
 }


    #pragma mark - Gesture recognizers

      - (void)handlePan:(UIPanGestureRecognizer *)gesture
 {
static CGPoint oldCenter;
CGPoint tranlation = [gesture translationInView:gesture.view];

if (gesture.state == UIGestureRecognizerStateBegan)
{
    oldCenter = self.circleCenter;
}

CGPoint newCenter = CGPointMake(oldCenter.x + tranlation.x, oldCenter.y + tranlation.y);

[self updateCirclePathAtLocation:newCenter radius:self.circleRadius];
 }

  - (void)handlePinch:(UIPinchGestureRecognizer *)gesture
 {
static CGFloat oldRadius;
CGFloat scale = [gesture scale];

if (gesture.state == UIGestureRecognizerStateBegan)
{
    oldRadius = self.circleRadius;
}

CGFloat newRadius = oldRadius * scale;

[self updateCirclePathAtLocation:self.circleCenter radius:newRadius];
}


 #pragma mark - UIGestureRecognizerDelegate

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
   {
      if ((gestureRecognizer == self.pan   && otherGestureRecognizer == self.pinch) ||
    (gestureRecognizer == self.pinch && otherGestureRecognizer == self.pan))
{
    return YES;
}

return NO;
 }

   - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
 }

   @end
Exaggerative answered 5/1, 2015 at 4:47 Comment(0)
B
1

enter image description here

In Swift 5 and SwiftUI this is how I solved it:

//
//  FlashlightView.swift
//
//  Created by Pascal Reitermann on 15.01.22.
//  Copyright 2022 Pascal Reitermann.
//

import SwiftUI

struct FlashlightView: View {
    let rect = UIScreen.main.bounds
    @State var center: CGPoint = CGPoint(x: UIScreen.screenWidth / 2, y: UIScreen.screenHeight / 2)

    var body: some View {
        ZStack {
            Rectangle()
                .fill(Color.black)
                .opacity(0.85)
                .mask(
                    holeShapeMask(in: rect)
                        .fill(style: FillStyle(eoFill: true))
                )
                .gesture(
                    dragLight
                )
        }
    }

    var dragLight: some Gesture {
        DragGesture()
            .onChanged { value in
                self.center = value.location
            }
    }

    func holeShapeMask(in rect: CGRect) -> Path {
        var path = Rectangle()
            .path(in: rect)
        if trunk.showLightBeam {
            path.move(to: center)
            path.addArc(
                center: center, radius: rect.width / 7, startAngle: .degrees(0), endAngle: .degrees(360),
                clockwise: false
            )
        }
        return path
    }

}

extension UIScreen {
   static let screenWidth = UIScreen.main.bounds.size.width
   static let screenHeight = UIScreen.main.bounds.size.height
}

You can use it in another class like this:

ZStack {
    Image("Background")
        .resizable()
        .aspectRatio(contentMode: .fill)
        .ignoresSafeArea()
    FlashlightView()
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .ignoresSafeArea()
}
Beeler answered 18/1, 2022 at 21:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.