Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/110.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/html/77.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 如何从CAShapeLayer获取坐标_Ios_Swift_Xcode_Calayer_Uibezierpath - Fatal编程技术网

Ios 如何从CAShapeLayer获取坐标

Ios 如何从CAShapeLayer获取坐标,ios,swift,xcode,calayer,uibezierpath,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 =

所以我正在努力做一个进度条。我画了一个圆形的路径,但是我希望点在进度条的末端,但是我如何得到点在当前进度的末端的位置呢

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
  }
}
这就是我得到的


这就是我想要的

你必须自己计算。因此,计算出圆弧起点和终点的角度:

let angle = (endAngle - startAngle) * progress + startAngle
然后使用基本三角法确定该点的位置:

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

dotLayer.position = point
顺便说一句,我建议将作为初始配置过程一部分的子层添加与作为视图布局过程一部分的更新路径分开,如果视图的框架发生变化,应用约束等,可以再次调用更新路径。因此,可能:

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

你得自己计算。因此,计算出圆弧起点和终点的角度:

let angle = (endAngle - startAngle) * progress + startAngle
然后使用基本三角法确定该点的位置:

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

dotLayer.position = point
顺便说一句,我建议将作为初始配置过程一部分的子层添加与作为视图布局过程一部分的更新路径分开,如果视图的框架发生变化,应用约束等,可以再次调用更新路径。因此,可能:

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

您需要的是旋转动画

let progressLayer = CAShapeLayer()
    let backLayer = CAShapeLayer()
    private func simpleShape() {
      let width: CGFloat = 15
      createCircle()

      //make circle transparant in middle
      progressLayer.fillColor = UIColor.clear.cgColor
      progressLayer.strokeColor = #colorLiteral(red: 0.888897419, green: 0.5411034822, blue: 0.04008810222, alpha: 1)
      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)
      backLayer.strokeEnd = 1

     // self.layer.addSublayer(gradientLayer)
    }

    private func createCircle() {

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


      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)


        let circleCenter = CGPoint(x:centerPoint.x - distance,y:centerPoint.y - circleRadius )

         let dotCircle = UIBezierPath()


        dotCircle.addArc(withCenter:circleCenter, radius: 3, startAngle: CGFloat(-90).deg2rad(), endAngle: CGFloat(270).deg2rad(), clockwise: true)

        dotLayer.path = dotCircle.cgPath
        dotLayer.position = CGPoint(x:centerPoint.x,y:centerPoint.y )
        dotLayer.strokeColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 0.6496753961)
        dotLayer.lineWidth = 10
        dotLayer.fillColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
        dotLayer.isHidden = true

      circle.layer.addSublayer(dotLayer)
    }

    let dotLayer = CAShapeLayer()

    public func setProgress(_ progress: CGFloat) {
        print(progress)
//      progressLayer.strokeEnd = progress
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.beginTime = CACurrentMediaTime() + 0.5;
        animation.fromValue = 0
        animation.toValue = progress
        animation.duration = 2
        animation.autoreverses = false
        animation.repeatCount = .nan
        animation.fillMode = .forwards
        animation.isRemovedOnCompletion = false
        progressLayer.add(animation, forKey: "line")


        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            self.dotLayer.isHidden = false
        let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
       // rotateAnimation.beginTime = CACurrentMediaTime() + 0.5;
        rotateAnimation.fromValue = (CGFloat( -90)).deg2rad()
        rotateAnimation.toValue = (360*progress - 98).deg2rad()
        rotateAnimation.duration = 2
        rotateAnimation.fillMode = .forwards
        rotateAnimation.isRemovedOnCompletion = false
        self.dotLayer.add(rotateAnimation, forKey: nil)
        }

    }

您需要的是旋转动画

let progressLayer = CAShapeLayer()
    let backLayer = CAShapeLayer()
    private func simpleShape() {
      let width: CGFloat = 15
      createCircle()

      //make circle transparant in middle
      progressLayer.fillColor = UIColor.clear.cgColor
      progressLayer.strokeColor = #colorLiteral(red: 0.888897419, green: 0.5411034822, blue: 0.04008810222, alpha: 1)
      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)
      backLayer.strokeEnd = 1

     // self.layer.addSublayer(gradientLayer)
    }

    private func createCircle() {

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


      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)


        let circleCenter = CGPoint(x:centerPoint.x - distance,y:centerPoint.y - circleRadius )

         let dotCircle = UIBezierPath()


        dotCircle.addArc(withCenter:circleCenter, radius: 3, startAngle: CGFloat(-90).deg2rad(), endAngle: CGFloat(270).deg2rad(), clockwise: true)

        dotLayer.path = dotCircle.cgPath
        dotLayer.position = CGPoint(x:centerPoint.x,y:centerPoint.y )
        dotLayer.strokeColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 0.6496753961)
        dotLayer.lineWidth = 10
        dotLayer.fillColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
        dotLayer.isHidden = true

      circle.layer.addSublayer(dotLayer)
    }

    let dotLayer = CAShapeLayer()

    public func setProgress(_ progress: CGFloat) {
        print(progress)
//      progressLayer.strokeEnd = progress
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.beginTime = CACurrentMediaTime() + 0.5;
        animation.fromValue = 0
        animation.toValue = progress
        animation.duration = 2
        animation.autoreverses = false
        animation.repeatCount = .nan
        animation.fillMode = .forwards
        animation.isRemovedOnCompletion = false
        progressLayer.add(animation, forKey: "line")


        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            self.dotLayer.isHidden = false
        let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
       // rotateAnimation.beginTime = CACurrentMediaTime() + 0.5;
        rotateAnimation.fromValue = (CGFloat( -90)).deg2rad()
        rotateAnimation.toValue = (360*progress - 98).deg2rad()
        rotateAnimation.duration = 2
        rotateAnimation.fillMode = .forwards
        rotateAnimation.isRemovedOnCompletion = false
        self.dotLayer.add(rotateAnimation, forKey: nil)
        }

    }

非常感谢。非常好用,只是想知道为什么选择进行扩展?嗯,扩展真的是组织代码的好方法。例如,您可以进行代码“折叠”,例如option+command+left折叠代码的各个部分。您可以将方法分组到逻辑部分在这种情况下,扩展是一组私有实用程序方法,将它们与常规生命周期和重写方法分开。另外,通过将扩展标记为私有扩展,可以避免将所有这些单独的方法标记为私有扩展。但是如果你不想使用扩展,你不必这么做。这只是一个很好的组织工具。谢谢!非常好用,只是想知道为什么选择进行扩展?嗯,扩展真的是组织代码的好方法。例如,您可以进行代码“折叠”,例如option+command+left折叠代码的各个部分。您可以将方法分组到逻辑部分在这种情况下,扩展是一组私有实用程序方法,将它们与常规生命周期和重写方法分开。另外,通过将扩展标记为私有扩展,可以避免将所有这些单独的方法标记为私有扩展。但是如果你不想使用扩展,你不必这么做。这只是一个很好的组织工具。