如何从 CAShapeLayer 获取坐标

Ben*_*son 1 xcode calayer ios uibezierpath swift

所以我正在尝试制作一个进度条。所以我制作了圆形路径,但我希望点位于进度条的末尾,但是如何使点的位置位于当前进度的末尾?

private func simpleShape() {
  let width: CGFloat = 10
  createCircle()

  //make circle transparant in middle
  progressLayer.fillColor = UIColor.clear.cgColor
  progressLayer.strokeColor = UIColor.blue.cgColor
  progressLayer.lineCap = CAShapeLayerLineCap.round
  progressLayer.lineWidth = width
  progressLayer.strokeStart = 0
  progressLayer.strokeEnd = 0

  //unfilled
  backLayer.lineWidth = width
  backLayer.strokeColor = #colorLiteral(red: 0.1411764706, green: 0.1725490196, blue: 0.2431372549, alpha: 1).cgColor
  backLayer.strokeEnd = 1

  self.layer.addSublayer(gradientLayer)
}

private func createCircle() {

  //create circle
  let circle = UIView(frame: bounds)
  circle.layoutIfNeeded()
  let centerPoint = CGPoint (x: circle.bounds.width / 2, y: circle.bounds.width / 2)
  let circleRadius: CGFloat = circle.bounds.width / 2 * 0.83

  let circlePath = UIBezierPath(arcCenter: centerPoint, radius: circleRadius, startAngle: CGFloat(-0.475 * Double.pi), endAngle: CGFloat(1.525 * Double.pi), clockwise: true)

  //add layers
  progressLayer.path = circlePath.cgPath
  backLayer.path = circlePath.cgPath
  circle.layer.addSublayer(backLayer)
  circle.layer.addSublayer(progressLayer)
  addSubview(circle)

  circle.layer.addSublayer(dotLayer)
}

let dotLayer = CAShapeLayer()

public func setProgress(_ progress: CGFloat) {
  progressLayer.strokeEnd = CGFloat(progress)

  if let progressEndpoint = progressLayer.path?.currentPoint {
    dotLayer.position = progressEndpoint
  }
}
Run Code Online (Sandbox Code Playgroud)

这就是我得到的

这就是我得到的

这就是我要的

这就是我要的

Rob*_*Rob 6

你\xe2\x80\x99必须自己计算。因此,请从弧的起始角和结束角算出角度:

\n\n
let angle = (endAngle - startAngle) * progress + startAngle\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后使用基本三角学来确定该点落在哪里:

\n\n
let point = CGPoint(x: centerPoint.x + radius * cos(angle),\n                    y: centerPoint.y + radius * sin(angle))\n\ndotLayer.position = point\n
Run Code Online (Sandbox Code Playgroud)\n\n

顺便说一句,我\xe2\x80\x99d建议将子层的添加(这是初始配置过程的一部分)与更新路径(这是视图布局过程的一部分,如果框架可能会再次调用)分开视图更改、应用约束等)。因此,也许:

\n\n
@IBDesignable\nclass ProgressView: UIView {\n    var progress: CGFloat = 0 { didSet { updateProgress() } }\n\n    private var centerPoint: CGPoint = .zero\n    private var radius: CGFloat = 0\n    private let startAngle: CGFloat = -0.475 * .pi\n    private let endAngle: CGFloat = 1.525 * .pi\n    private let lineWidth: CGFloat = 10\n\n    private lazy var progressLayer: CAShapeLayer = {\n        let shapeLayer = CAShapeLayer()\n        shapeLayer.fillColor = UIColor.clear.cgColor\n        shapeLayer.strokeColor = UIColor.blue.cgColor\n        shapeLayer.lineCap = .round\n        shapeLayer.lineWidth = lineWidth\n        shapeLayer.strokeStart = 0\n        shapeLayer.strokeEnd = progress\n        return shapeLayer\n    }()\n\n    private lazy var backLayer: CAShapeLayer = {\n        let shapeLayer = CAShapeLayer()\n        shapeLayer.lineWidth = lineWidth\n        shapeLayer.strokeColor = #colorLiteral(red: 0.1411764706, green: 0.1725490196, blue: 0.2431372549, alpha: 1).cgColor\n        return shapeLayer\n    }()\n\n    private lazy var dotLayer: CAShapeLayer = {\n        let shapeLayer = CAShapeLayer()\n        shapeLayer.path = UIBezierPath(arcCenter: .zero, radius: lineWidth / 2 * 1.75, startAngle: 0, endAngle: 2 * .pi, clockwise: true).cgPath\n        shapeLayer.fillColor = UIColor.white.withAlphaComponent(0.5).cgColor\n        return shapeLayer\n    }()\n\n    override init(frame: CGRect) {\n        super.init(frame: frame)\n        addSublayers()\n    }\n\n    required init?(coder: NSCoder) {\n        super.init(coder: coder)\n        addSublayers()\n    }\n\n    override func layoutSubviews() {\n        super.layoutSubviews()\n\n        updatePaths()\n        updateProgress()\n    }\n}\n\nprivate extension ProgressView {\n    func addSublayers() {\n        layer.addSublayer(backLayer)\n        layer.addSublayer(progressLayer)\n        layer.addSublayer(dotLayer)\n    }\n\n    func updatePaths() {\n        centerPoint = CGPoint(x: bounds.midX, y: bounds.midY)\n        radius = min(bounds.width, bounds.height) / 2 * 0.83\n\n        let circlePath = UIBezierPath(arcCenter: centerPoint, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)\n\n        progressLayer.path = circlePath.cgPath\n        backLayer.path = circlePath.cgPath\n    }\n\n    func updateProgress() {\n        progressLayer.strokeEnd = progress\n\n        let angle = (endAngle - startAngle) * progress + startAngle\n        let point = CGPoint(x: centerPoint.x + radius * cos(angle),\n                            y: centerPoint.y + radius * sin(angle))\n\n        dotLayer.position = point\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n