1
votes

Before asking the question, i have searched the SO:

iPhone-Move UI Image view along a Bezier curve path

But it did not give a explicit answer.

My requirement is like this, I have a bezier path, and a view(or layer if OK), I want to add pan gesture to the view, and the view(or layer)'s move path must constraint to the bezier path.

enter image description here

My code is below:

The MainDrawView.swift:

import UIKit

class MainDrawView: UIView {


    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func draw(_ rect: CGRect) {
        // Drawing code

        drawArc()
    }

    func drawArc() {

        let context = UIGraphicsGetCurrentContext()

        // set start point
        context?.move(to: CGPoint.init(x: 0, y: 400))
        //draw curve
        context?.addQuadCurve(to: CGPoint.init(x: 500, y: 250), control: CGPoint.init(x: UIScreen.main.bounds.size.width, y: 200))

        context?.strokePath()

    }
}

The ViewController.swift:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var clickButton: UIButton!

    lazy var view1:UIView = {

        let view: UIView = UIView.init(frame: CGRect.init(origin: CGPoint.init(x: 0, y: 0), size: CGSize.init(width: 10, height: 10)))
        view.center = CGPoint.init(x: UIScreen.main.bounds.size.width / 2.0, y: UIScreen.main.bounds.size.height / 2.0)
        view.backgroundColor = UIColor.red

        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        initData()
        initUI()
    }

    func initData() {

    }

    func initUI() {

        self.clickButton.isHidden = true

        // init the view
        self.view.addSubview(self.view1)
    }

}

Edit -1

My deep requirment is when I use pan guester move the view/layer, the view will move on the bezier path.

2
Can it be CALayers? Much easier to accomplish - at least in my opinion - that UIViews. A single UIView can have several CALayers. If this works for you I have code that may do the trick.dfd
@dfd, ok, use CALayer also can implement it.aircraft
@aircraft - So do I understand you correctly, that you're trying to have the user activate a UIPanGestureRecognizer, and as that updates, the square will move along the path?Pierce
@Pierce, great, that is my meaning.aircraft

2 Answers

3
votes

If bezier path is off lines then u can find the slope of the line and for every change in x or y you can calculate the position of the bezier path var y :Float = (slope * (xPos-previousCoord.x))+previousCoord.y; xPos is continuously changing. Similarly, u can find for x. For any closed shape with line segments, you can use this.

But if u need it for circle, then u need to convert cartesian to polar. i.e.., from coordinates u can find the angle, then from that angle, you have the radius so by using that angle u need to find cartesian coordinates from that. enter image description here

θ = tan-1 ( 5 / 12 )

U need to use mainly 3 coordinates one is centre of circle, the second one is your touch point, and the last one is (touchpoint.x, centreofcircle.y). from centre of circle calculate distance between two coordinates You have θ and radius of circle then find points using

x = r × cos( θ )

y = r × sin( θ )

Don't mistake r in the image for the radius of the circle, r in the image is the hypotenuse of three coordinates. You should calculate for every change of x in touch point.

Hope it works. But for irregular shapes I am not sure how to find.

0
votes

It sounds like you'll probably have to do some calculations. When you set your method to handle the UIPanGestureRecognizer, you can get a vector back for the pan, something like this:

func panGestureRecognized(_ sender: UIPanGestureRecognizer) {
    let vector:CGPoint = sender.translation(in: self.view) // Or use whatever view the UIPanGestureRecognizer was added to
}

You could then use that to extrapolate the movement along the x and y axis. I would recommend starting by getting an algebraic equation for the path you're moving the view on. To move the view along that line, you're going to have to be able to calculate a point along that line relative to the movement of the UIPanGestureRecognizer, and then update the position of the view using that calculated point along the line.

I don't know if it'll work for what you're trying to do, but if you want to try animating your view along the path, it's actually pretty easy:

let path = UIBezierPath() // This would be your custom path

let animation = CAKeyframeAnimation(keyPath: "position")
animation.path = path.cgPath
animation.fillMode = kCAFillModeForwards
animation.isRemovedOnCompletion = false     animation.repeatCount = 0
animation.duration = 5.0 // However long you want
animation.speed = 2  // However fast you want
animation.calculationMode = kCAAnimationPaced
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)

animatingView.layer.add(animation, forKey: "moveAlongPath")