Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Here's one concept. Draws a line from where you start dragging your finger until where you let go, animating it as you drag your finger around. It does this by making a <code>CAShapeLayer</code>, resetting the <code>path</code> as you move your finger around. </p> <p>This should demonstrate the basic idea:</p> <pre><code>- (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; } } </code></pre> <p>Or, in Swift 3:</p> <pre><code>override func viewDidLoad() { super.viewDidLoad() let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:))) view.addGestureRecognizer(pan) } private func createShapeLayer(for view: UIView) -&gt; 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 } } </code></pre> <hr> <p>If you don't use <code>CAShapeLayer</code>, but you want to keep track of previous paths, you'll have to maintain an array for those old paths, and build a path that consists of all of the old paths, perhaps something like:</p> <pre><code>@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) &amp;&amp; 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 </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

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