How to constraint a view/layer's move path to a Bezier curve path?

时间:2017-01-10 02:28:50

标签: ios swift uibezierpath

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 个答案:

答案 0 :(得分:2)

如果bezier路径偏离线路,则可以找到线路的斜率,并且对于x或y的每个变化,您都可以计算贝塞尔路径的位置 var y :Float = (slope * (xPos-previousCoord.x))+previousCoord.y; xPos正在不断变化。同样,你可以找到x。对于任何带有线段的封闭形状,您可以使用它。

但如果你需要圈子,那么你需要将笛卡儿转换为极地。即..,从坐标u可以找到角度,然后从那个角度,你有半径,所以通过使用该角度你需要从那里找到笛卡尔坐标。 enter image description here

θ= tan-1(5/12)

你需要主要使用3个坐标,一个是圆心,第二个是你的触点,最后一个是(touchpoint.x,centreofcircle.y)。从圆心开始计算两个坐标之间的距离 你有θ和圆的半径,然后使用

找到点

x = r×cos(θ)

y = r×sin(θ)

不要将图像中的r误认为圆的半径,图像中的r是三个坐标的斜边。你应该计算接触点x的每次变化。

希望它有效。但对于不规则的形状,我不知道如何找到。

答案 1 :(得分:0)

听起来你可能不得不做一些计算。当您设置方法来处理UIPanGestureRecognizer时,您可以为平移器获取一个向量,如下所示:

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

然后,您可以使用它来推断沿x和y轴的移动。我建议首先得到你正在移动视图的路径的代数方程式。要沿着该线移动视图,您将必须能够计算相对于UIPanGestureRecognizer移动的该线的点,然后使用沿着该点的计算点更新视图的位置。线。

我不知道它是否适用于您正在尝试的内容,但如果您想尝试沿着路径制作动画,那实际上非常简单:

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")