Reposition CGPath/UIBezierPath in View
Asked Answered
C

3

13

How can I update position of an already drawn CGPath/UIBezierPath on a view? I would like to move or change a path's position and then perhaps call the drawInRect method to just render the path again.

Cyrilla answered 18/6, 2013 at 12:31 Comment(2)
What are they drawn on? The views layer? A sub layer? A shape layer?Wsan
They are being drawn on a views layer.Cyrilla
B
25

From your question it sounds like you are drawing the path using Core Graphics inside drawRect: (as compared to using a CAShapeLayer) so I'll explain the version first.

Moving a CGPath

You can create a new path by transforming another path. A translation transform moves the transformed object a certain distance in x and y. So using a translation transform you can move your existing path a certain number of points in both x and y.

CGAffineTransform translation = CGAffineTransformMakeTranslation(xPixelsToMove,
                                                                 yPixelsToMove);
CGPathRef movedPath = CGPathCreateCopyByTransformingPath(originalCGPath,
                                                         &translation);

Then you could use the movedPath to draw the same way you are already doing.

You could also change modify the same path

yourPath = CGPathCreateCopyByTransformingPath(yourPath,
                                              &translation);

and simply redraw it.

Moving a shape layer

If you are using a shape layer, moving it is even easier. Then you only have to change the position of the layer using the position property.

Update:

If you want to use a shape layer you can simply create a new CAShapeLayer and set its path to be your CGPath. You will need QuartzCore.framework for this since CAShapeLayer is part of Core Animation.

CAShapeLayer *shape = [CAShapeLayer layer];
shape.path = yourCGParth;
shape.fillColor = [UIColor redColor].CGColor;

[someView.layer addSublayer:shape];

Then to move the shape you simply change its position.

shape.position = thePointYouWantToMoveTheShapeTo;
Bareilly answered 18/6, 2013 at 13:41 Comment(11)
I tried the first code you suggested CGAffineTransformMakeTranslation but with the current x and y of my touch as the parameters. It doesnt seem to be following the touch location and is kinda incrementing the position of the path.Cyrilla
@jeraldov You should pass in the difference in x and y. For example the translationInView: for a pan gesture recognizer. That said, if you are going to move the shape a lot you should consider a shape layer.Prefecture
Yes I'll be moving the shape a lot since I want to drag/move a shape on TouchesMoved event. I will try to go with the shape layer route. Can you give me a sample or a tutorial on how to do shape layers? Thanks.Cyrilla
@jeraldov I've updated my question. Does it explain enough about shape layers?Prefecture
Yes it does. I am now able to reposition my drawings.Cyrilla
Hi. I run into some problem again, it seems like if I change the position of the shape, it doesnt appear on the view. How do I update the position of the view and make the change visible.Cyrilla
Hello @DavidRönnqvist, can this logic be used for undo/redo like when we do undo, move the path out of the screen and when we do redo bring back to its initial position. Not sure just a thoughtWacke
@Wacke in theory it could but creating a new copy of the path and just moving it off screen for every step that can be undone/redone could introduce performance problems, so it's best to prototype and measure first.Prefecture
ok @DavidRönnqvist, could be please look at this #21439086, I am totally stuck with itWacke
could you show your implementation in Swift too? I am confused about &translation expressionSupercolumnar
The translated path has incorrect bounds, lets say I moved a layer from point(0.0) to point(100, 100), but when I print the boundingbox of cgpath its some where around point(150, 200). any idea whats the issue.Udder
N
8

/// Translate cgPath from its center to give point.No need to move shape layer .Moving path will make app smooth

func translate(path : CGPath?, by point: CGPoint) -> CGPath? {

    let bezeirPath = UIBezierPath()
    guard let prevPath = path else {
        return nil
    }
    bezeirPath.cgPath = prevPath
    bezeirPath.apply(CGAffineTransform(translationX: point.x, y: point.y))

    return bezeirPath.cgPath
}
Nessus answered 13/5, 2018 at 7:38 Comment(1)
The translated path has incorrect bounds, lets say I moved a layer from point(0.0) to point(100, 100), but when I print the boundingbox of cgpath its some where around point(150, 200). any idea whats the issue.Udder
H
0

I did it a bit different from other answers above.

Let's suppose you want to move a circle, first I define its center coordinates and radius in global variables, like this:

var center: CGPoint = CGPoint(x: UIScreen.main.bounds.width/2, y: UIScreen.main.bounds.height/2)
var radius: CGFloat = 10

I draw this circle like in the code bellow:

override func draw(_ rect: CGRect) {
    super.draw(rect)  
    drawCircle()
}

func drawCircle() {
    let circle = UIBezierPath(
        arcCenter: self.center,
        radius: CGFloat(self.radius),
        startAngle: CGFloat(0),
        endAngle: CGFloat(2*Double.pi),
        clockwise: true)

    let circleLayer = CAShapeLayer()
        circleLayer.path = circle.cgPath
        circleLayer.fillColor = UIColor.clear.cgColor
        circleLayer.strokeColor = UIColor.black.cgColor
        circleLayer.lineWidth = 10.0
        layer.addSublayer(circleLayer)
}

Inside the UIView, I have a touchesMoved(), like this:

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?){
    if let touch = touches.first {
        let pos = touch.location(in: self)

        if (pow(pos.x-self.center.x,2) + pow(pos.y-self.center.y,2) <= pow(self.radius,2)){
                self.center = CGPoint(x: pos.x, y: pos.y)
        }

        setNeedsDisplay()
    }
}

Everytime the touch is inside the circle, its center coordinates are updated and the UIView is redrawn with setNeedDisplay(), so the circle moves.

Hope it helps someone!

Hoitytoity answered 4/6, 2020 at 17:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.