【问题标题】:Best way to draw circle on top of UIImageView IOS在 UIImageView IOS 上绘制圆圈的最佳方法
【发布时间】:2013-03-10 13:15:03
【问题描述】:

我有一个 UIImageView 显示用户刚刚用相机拍摄的照片。在该图像上,我需要绘制一个设置大小的透明可移动圆圈。因此,当用户在图像上拖动手指时,圆圈会随之移动。然后,如果用户停止移动手指,圆圈就会停留在原处。

我一直在阅读CAShapeLayer 上的苹果文档,但我仍然不确定最好的方法,我应该绘制 UIView 吗?

任何例子都会很棒。谢谢。

【问题讨论】:

    标签: ios uiview uiimageview cgcontext cashapelayer


    【解决方案1】:

    以下代码创建三个手势:

    • 点击手势会在视图上放置一个圆圈(这样您就可以看到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
    

    【讨论】:

    • 谢谢 Rob,非常清楚,正是我所追求的,如果可以的话,我会打两下 :-)
    • @Rob 上面的代码很棒,你能帮我了解如何将圆圈下的图像部分裁剪成单独的图像吗?谢谢
    • @Rob 还有gesture.state = UIGestureRecognizerStateCancelled;在 ios7 上报错
    • @vishal 重新裁剪图像视图,您可以为图像视图的图层设置蒙版。将裁剪后的图像重新保存到新图像中,您可以使用 drawViewHierarchyInRectrenderInContext(在 S.O. 中搜索这些术语,您会找到很多示例)。回复UIGestureRecognizerStateCancelled,你做了必要的#import &lt;UIKit/UIGestureRecognizerSubclass.h&gt;吗?我建议您对这些主题进行一些挖掘,如果您仍然无法弄清楚如何去做,请发布您自己的问题,因为我们超出了这个特定问题的范围。
    • 这是一个新问题@Rob,我实际上做不到,我不是一个经验丰富的 iOS 程序员,因此有点困惑,我已经发布了屏幕以及我正在使用的代码,请看看你是否可以提供一些相同上下文中的代码stackoverflow.com/questions/20165906/…
    猜你喜欢
    • 2015-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-15
    • 1970-01-01
    • 2015-12-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多