objective-c - 有弹性的 UIBezierPath 线?

标签 objective-c ios uibezierpath touchesbegan touchesmoved

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

因此,如果我触摸屏幕中间并将手指滑出,则当我的手指在屏幕上移动时,一条线似乎会“拉伸(stretch)”并围绕原点旋转。当我抬起手指时。目的地点应该最终确定并创建一条线。我可以在屏幕上拖动手指并在屏幕上“绘图”,但这不是我想要做的。

我认为 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];
}

最佳答案

这是一个概念。从您开始拖动手指的位置到您放手的位置绘制一条线,并在您四处拖动手指时对其进行动画处理。它通过创建一个 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

关于objective-c - 有弹性的 UIBezierPath 线?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14202718/

相关文章:

ios - 如何使用一个 UIBezierPath 创建两个不同颜色的笔画

ios - UIImage 内部自定义形状和动画

ios - AFNetworking 将参数作为表单数据发送

ios - 从选择器变量中查找参数类/类型 - Objective-C

objective-c - IBAction 中属性(property)失去值(value)

ios - IOS应用发布前如何在开发者帐号中查找Appid

objective-c - 静态变量和内存消耗

iphone - 关闭uiimagepickercontroller后如何使状态栏重新出现

ios - 您如何判断用户使用的是 iPhone 6 还是 6+

ios - 如何让 CAShapeLayer 的框架面对它的 super View ?