ios - 如何从 CAShapeLayer 获取坐标

标签 ios swift xcode calayer uibezierpath

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

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
  }
}

This is what I'm getting

这就是我得到的

This is what I want

这就是我要的

最佳答案

你将不得不自己计算它。因此,从弧的开始角度和结束角度找出角度:

let angle = (endAngle - startAngle) * progress + startAngle

然后使用基本三角函数来确定该点落在哪里:
let point = CGPoint(x: centerPoint.x + radius * cos(angle),
                    y: centerPoint.y + radius * sin(angle))

dotLayer.position = point

顺便说一句,我建议将添加子层(这是初始配置过程的一部分)与更新路径(这是 View 布局过程的一部分,如果 View 的框架发生变化,可能会再次调用)分开,应用约束等)。因此,也许:
@IBDesignable
class ProgressView: UIView {
    var progress: CGFloat = 0 { didSet { updateProgress() } }

    private var centerPoint: CGPoint = .zero
    private var radius: CGFloat = 0
    private let startAngle: CGFloat = -0.475 * .pi
    private let endAngle: CGFloat = 1.525 * .pi
    private let lineWidth: CGFloat = 10

    private lazy var progressLayer: CAShapeLayer = {
        let shapeLayer = CAShapeLayer()
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = UIColor.blue.cgColor
        shapeLayer.lineCap = .round
        shapeLayer.lineWidth = lineWidth
        shapeLayer.strokeStart = 0
        shapeLayer.strokeEnd = progress
        return shapeLayer
    }()

    private lazy var backLayer: CAShapeLayer = {
        let shapeLayer = CAShapeLayer()
        shapeLayer.lineWidth = lineWidth
        shapeLayer.strokeColor = #colorLiteral(red: 0.1411764706, green: 0.1725490196, blue: 0.2431372549, alpha: 1).cgColor
        return shapeLayer
    }()

    private lazy var dotLayer: CAShapeLayer = {
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = UIBezierPath(arcCenter: .zero, radius: lineWidth / 2 * 1.75, startAngle: 0, endAngle: 2 * .pi, clockwise: true).cgPath
        shapeLayer.fillColor = UIColor.white.withAlphaComponent(0.5).cgColor
        return shapeLayer
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        addSublayers()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        addSublayers()
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        updatePaths()
        updateProgress()
    }
}

private extension ProgressView {
    func addSublayers() {
        layer.addSublayer(backLayer)
        layer.addSublayer(progressLayer)
        layer.addSublayer(dotLayer)
    }

    func updatePaths() {
        centerPoint = CGPoint(x: bounds.midX, y: bounds.midY)
        radius = min(bounds.width, bounds.height) / 2 * 0.83

        let circlePath = UIBezierPath(arcCenter: centerPoint, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)

        progressLayer.path = circlePath.cgPath
        backLayer.path = circlePath.cgPath
    }

    func updateProgress() {
        progressLayer.strokeEnd = progress

        let angle = (endAngle - startAngle) * progress + startAngle
        let point = CGPoint(x: centerPoint.x + radius * cos(angle),
                            y: centerPoint.y + radius * sin(angle))

        dotLayer.position = point
    }
}

关于ios - 如何从 CAShapeLayer 获取坐标,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60509975/

相关文章:

ios - 使用 "AssumeRole"api AWS iOS sdk 生成临时凭证

arrays - Xcode 在处理文件时卡住

ios - 如何在我的 tableView 单元格中进行自定义操作?

objective-c - 如何停止在每次函数调用时都创建新字典以减少内存使用量?

ios - SKScene didChangeSize : called multiple times

ios - 将变量值传递给 UIFont systemFontOfSize 函数

ios - 单元格在滚动后丢失 gradientLayer

ios - 动画(旋转)UIBarButtonItem 自定义按钮

ios - 从元组的 Swift 数组中投影一列

swift - 升级 Xcode 后 cocoapods 的问题