Stretchy UIBezierPath线?

时间:2013-01-07 19:30:06

标签: objective-c ios uibezierpath touchesbegan touchesmoved

我想用我的手指绘制一条直线,根据我离原点的距离自动调整大小。

因此,如果我触摸中间的屏幕并滑出我的手指,当我的手指在屏幕上移动时,一条线似乎“伸展”并绕着orgin点转动。当我举起手指时。目标点应该最终确定并创建一条线。我可以将手指拖过屏幕并在屏幕上“绘制”,但这不是我想要做的。

我认为UIBeizerPath moveToPoint会有所帮助,但它只会让事情变得混乱。

我做错了什么?

- (id)initWithFrame:(CGRect)frame
{
     //default line properties
    myPath=[[UIBezierPath alloc]init];
    myPath.lineCapStyle=kCGLineCapRound;
    myPath.miterLimit=0;
    myPath.lineWidth=lineWidth;
    brushPattern=[UIColor blackColor];
 }



-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    CGPoint curPoint = [[touches anyObject] locationInView:self];

    lastPoint = curPoint;

    [myPath moveToPoint:lastPoint];
    [pathArray addObject:myPath];
    [self setNeedsDisplay];
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{

    CGPoint curPoint = [[touches anyObject] locationInView:self];

    myPath.lineWidth=lineWidth;
    brushPattern=[UIColor redColor]; //red to show it hasn't been added yet.
    [myPath moveToPoint:tempPoint];
    [self setNeedsDisplay];
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

    CGPoint curPoint = [[touches anyObject] locationInView:self];

    myPath.lineWidth=lineWidth;

    brushPattern=[UIColor blackColor]; //finalize the line with black color
    [myPath addLineToPoint:curPoint];
    [self setNeedsDisplay];
}

2 个答案:

答案 0 :(得分:4)

这是一个概念。从您开始拖动手指的位置绘制一条直线,直到您放开手指为止,在您拖动手指时为其设置动画。这是通过制作CAShapeLayer来实现的,在您移动手指时重置path

这应该表明基本的想法:

- (void)viewDidLoad {
    [super viewDidLoad];

    UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
    [self.view addGestureRecognizer:gesture];
}

- (CAShapeLayer *)createShapeLayer:(UIView *)view {
    CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];

    shapeLayer.fillColor = [UIColor clearColor].CGColor;
    shapeLayer.strokeColor = [UIColor redColor].CGColor;
    shapeLayer.lineWidth = 3.0;

    [view.layer addSublayer:shapeLayer];

    return shapeLayer;
}

- (void)handlePanGesture:(UIPanGestureRecognizer *)gesture {
    static CAShapeLayer *shapeLayer;
    static CGPoint origin;

    if (gesture.state == UIGestureRecognizerStateBegan) {
        shapeLayer = [self createShapeLayer:gesture.view];
        origin = [gesture locationInView:gesture.view];
    } else if (gesture.state == UIGestureRecognizerStateChanged) {
        UIBezierPath *path = [UIBezierPath bezierPath];
        [path moveToPoint:origin];
        CGPoint location = [gesture locationInView:gesture.view];
        [path addLineToPoint:location];
        shapeLayer.path = path.CGPath;
    } else if (gesture.state == UIGestureRecognizerStateEnded ||
               gesture.state == UIGestureRecognizerStateFailed ||
               gesture.state == UIGestureRecognizerStateCancelled) {
        shapeLayer = nil;
    }
}

或者,在Swift 3中:

override func viewDidLoad() {
    super.viewDidLoad()

    let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
    view.addGestureRecognizer(pan)
}

private func createShapeLayer(for view: UIView) -> CAShapeLayer {
    let shapeLayer = CAShapeLayer()
    shapeLayer.fillColor = UIColor.clear.cgColor
    shapeLayer.strokeColor = UIColor.red.cgColor
    shapeLayer.lineWidth = 3.0

    view.layer.addSublayer(shapeLayer)

    return shapeLayer
}

private var shapeLayer: CAShapeLayer!
private var origin: CGPoint!

func handlePan(_ gesture: UIPanGestureRecognizer) {
    if gesture.state == .began {
        shapeLayer = createShapeLayer(for: gesture.view!)
        origin = gesture.location(in: gesture.view)
    } else if gesture.state == .changed {
        let path = UIBezierPath()
        path.move(to: origin)
        path.addLine(to: gesture.location(in: gesture.view))
        shapeLayer.path = path.cgPath
    } else if gesture.state == .ended || gesture.state == .failed || gesture.state == .cancelled {
        shapeLayer = nil
    }
}

如果您不使用CAShapeLayer,但想要跟踪以前的路径,则必须为这些旧路径维护一个数组,并构建一个包含所有旧路径的路径,或许类似于:

@interface CustomView ()

@property (nonatomic) CGPoint originPoint;
@property (nonatomic) CGPoint currentPoint;
@property (nonatomic) NSMutableArray *previousPaths;

@end

@implementation CustomView

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self configure];
    }
    return self;
}

- (id)init {
    return [self initWithFrame:CGRectZero];
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self configure];
    }
    return self;
}

- (void)configure {
    _previousPaths = [[NSMutableArray alloc] init];
}

- (void)drawRect:(CGRect)rect {
    [[UIColor redColor] setStroke];

    UIBezierPath *drawPath = [UIBezierPath bezierPath];

    drawPath.lineCapStyle = kCGLineCapRound;
    drawPath.miterLimit = 0;
    drawPath.lineWidth = 3.0;

    for (UIBezierPath *path in self.previousPaths)
        [drawPath appendPath:path];

    UIBezierPath *path = [self pathForCurrentLine];
    if (path)
        [drawPath appendPath:path];

    [drawPath stroke];
}

- (UIBezierPath *)pathForCurrentLine {
    if (CGPointEqualToPoint(self.originPoint, CGPointZero) && CGPointEqualToPoint(self.currentPoint, CGPointZero))
        return nil;

    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:self.originPoint];
    [path addLineToPoint:self.currentPoint];

    return path;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    self.originPoint = [[touches anyObject] locationInView:self];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    if ([event respondsToSelector:@selector(predictedTouchesForTouch:)]) {
        touch = [[event predictedTouchesForTouch:touch] lastObject] ?: touch;
    }
    self.currentPoint = [touch locationInView:self];

    [self setNeedsDisplay];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    self.currentPoint = [[touches anyObject] locationInView:self];
    [self.previousPaths addObject:[self pathForCurrentLine]];
    self.originPoint = self.currentPoint = CGPointZero;

    [self setNeedsDisplay];
}

@end

答案 1 :(得分:2)

UIBezierPath正在根据您的指示构建路径。想象一支钢笔。当你说“moveToPoint:”时,它会将笔移动到那一点。当你说“lineToPoint:”时,它会将笔放下并将其从当前位置移动到新点。等等。

为了获得您想要的效果,您需要在触摸移动时创建一个新路径,从原始点到当前触摸位置绘制一条线。