在UIImageView IOS上绘制圆圈的最佳方法

时间:2013-03-10 13:15:04

标签: ios uiview uiimageview cgcontext cashapelayer

我有一个UIImageView,显示用户刚用相机拍摄的照片。在该图像上,我需要绘制一个设定大小的透明可移动圆圈。因此,当用户将手指拖过图像时,圆圈随之移动。然后,如果用户停止移动他们的手指,则圆圈保持在同一位置。

我一直在阅读关于CAShapeLayer的苹果文档,但我还是不确定最好的方法,我应该画一个UIView吗?

任何例子都很棒。感谢。

1 个答案:

答案 0 :(得分:19)

以下代码创建了三个手势:

  • 点击手势会在视图上放置一个圆圈(这样您就可以看到CAShapeLayer是如何创建的);

  • 平移手势会移动圆圈(假设您开始从圆圈内移动);以及

  • 捏合手势会调整圆圈的大小。

因此可能看起来像:

#import <QuartzCore/QuartzCore.h>
#import <UIKit/UIGestureRecognizerSubclass.h>

@interface ViewController ()

@property (nonatomic, weak) CAShapeLayer *circleLayer;
@property (nonatomic) CGPoint circleCenter;
@property (nonatomic) CGFloat circleRadius;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // create tap gesture

    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                                          action:@selector(handleTap:)];
    [self.view addGestureRecognizer:tap];

    // create pan gesture

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

    // create pinch gesture

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

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

// Create a CAShapeLayer for our circle on tap on the screen

- (void)handleTap:(UITapGestureRecognizer *)gesture
{
    CGPoint location = [gesture locationInView:gesture.view];

    // if there was a previous circle, get rid of it

    [self.circleLayer removeFromSuperlayer];

    // create new CAShapeLayer

    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.path = [[self makeCircleAtLocation:location radius:50.0] CGPath];
    shapeLayer.strokeColor = [[UIColor redColor] CGColor];
    shapeLayer.fillColor = nil;
    shapeLayer.lineWidth = 3.0;

    // Add CAShapeLayer to our view

    [gesture.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;
}

// Let's move the CAShapeLayer on a pan gesture (assuming we started
// pan inside the circle).

- (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.

        if (distance > self.circleRadius)
            gesture.state = UIGestureRecognizerStateCancelled;
        else
            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);

        // Update the path for our CAShapeLayer

        self.circleLayer.path = [[self makeCircleAtLocation:newCenter radius:self.circleRadius] CGPath];
    }
}

// Let's resize circle in the CAShapeLayer on a pinch gesture (assuming we have
// a circle layer).

- (void)handlePinch:(UIPinchGestureRecognizer *)gesture
{
    static CGFloat oldCircleRadius;

    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        if (self.circleLayer)
            oldCircleRadius = self.circleRadius;
        else
            gesture.state = UIGestureRecognizerStateCancelled;
    }
    else if (gesture.state == UIGestureRecognizerStateChanged)
    {
        CGFloat newCircleRadius = oldCircleRadius * gesture.scale;
        self.circleLayer.path = [[self makeCircleAtLocation:self.circleCenter radius:newCircleRadius] CGPath];
    }
}

@end