-1

I am using bezier path, shape layer and CABasicAnimation to draw view animately. But it draw view only single time. How to draw same bezier path multiple times after finished the previous one ?

This is the attached code for creating this type of waveform. Please check...

enter image description here

class ViewController: UIViewController {

@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var graphView: UIView!

// MARK:- PROPERTIES
var movingPoint: CGFloat = 0.0
var startInterval: CGFloat = 10.0
var noOfIntervals: CGFloat = 0
var intervalWidth: CGFloat = 0
var pWaveWidth: CGFloat = 20.0
var qrsWaveWidth: CGFloat = 15
var tWaveWidth: CGFloat = 30
var gapBetweenTwoIntervals: CGFloat = 50
let shapeLayer = CAShapeLayer()
var bezierPath = UIBezierPath()
let animation = CABasicAnimation(keyPath: "strokeEnd")



override func viewDidLoad() {
    super.viewDidLoad()
    
    graphView.translatesAutoresizingMaskIntoConstraints = true
    graphView.frame.size = CGSize(width: 100000, height: 150)
    DispatchQueue.main.async {
        self.graphView.layoutIfNeeded()
    }
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    createGraph()
}


@objc func createGraph() {
    shapeLayer.fillColor = UIColor.clear.cgColor
    shapeLayer.strokeColor = UIColor.red.cgColor
    shapeLayer.lineWidth = 2.0
    _ = createBezierPath()
    graphView.layer.addSublayer(shapeLayer)
}

@discardableResult
@objc func createBezierPath(isPathAlreadyCreated: Bool = false) -> UIBezierPath {
    
    for _ in 0..<205 {
        let path = UIBezierPath()
        path.move(to: CGPoint(x: movingPoint, y: graphView.halfHeight))
        path.addLine(to: CGPoint(x: startInterval, y: graphView.halfHeight))
        
        // P Point Curve
        let pWaveX = startInterval + pWaveWidth
        path.addQuadCurve(to: CGPoint(x: pWaveX, y: graphView.halfHeight), controlPoint: CGPoint(x: pWaveX - 6.0, y: graphView.halfHeight - 30.0))
        
        let gapBetweenPAndQRS: CGFloat = 20.0
        path.addLine(to: CGPoint(x: (pWaveX + gapBetweenPAndQRS), y: graphView.halfHeight))
        
        // QRS
        let qrsStartPoint = pWaveX + gapBetweenPAndQRS
        let qrsComplex = qrsStartPoint + qrsWaveWidth
        let qrsComplexEndPoint = qrsComplex + (qrsWaveWidth / 2)
        path.addLine(to: CGPoint(x: qrsStartPoint + (qrsWaveWidth / 2), y: graphView.halfHeight - 70.0))
        path.addLine(to: CGPoint(x: qrsComplex, y: graphView.halfHeight + 40.0))
        path.addLine(to: CGPoint(x: qrsComplexEndPoint, y: graphView.halfHeight))
        
        let gapBetweenQRSAndTWave: CGFloat = 20.0
        let tWaveStartPoint = qrsComplexEndPoint + gapBetweenQRSAndTWave
        path.addLine(to: CGPoint(x: tWaveStartPoint, y: graphView.halfHeight))
        
        // T Point Curve
        let tWaveEndX = tWaveStartPoint + tWaveWidth
        print(tWaveEndX)
        path.addQuadCurve(to: CGPoint(x: tWaveEndX, y: graphView.halfHeight), controlPoint: CGPoint(x: tWaveEndX - 7.0, y: graphView.halfHeight - 30.0))
        
        movingPoint = tWaveEndX
        startInterval = tWaveEndX + gapBetweenTwoIntervals
        path.addLine(to: CGPoint(x: startInterval, y: graphView.halfHeight))
        bezierPath.append(path)
    }
    
    shapeLayer.path = bezierPath.cgPath
    
    animation.fromValue = 0.0
    animation.duration = 205 * 2
    shapeLayer.add(animation, forKey: "PathAnimation")
    
    return bezierPath
}

}

Rahul Chopra
  • 187
  • 1
  • 13

1 Answers1

0

I managed to solve your problem, please check and let me know if it works for you.

NOTE - Instead of server response I have added a button click to add more graph patterns. You can use the server response to do the same by calling the button tap code from server response success method.

import UIKit

class GraphViewController: UIViewController {

@IBOutlet weak var graphView: UIView!

// MARK:- PROPERTIES
var movingPoint: CGFloat = 0.0
var startInterval: CGFloat = 10.0
var noOfIntervals: CGFloat = 0
var intervalWidth: CGFloat = 0
var pWaveWidth: CGFloat = 20.0
var qrsWaveWidth: CGFloat = 15
var tWaveWidth: CGFloat = 30
var gapBetweenTwoIntervals: CGFloat = 50
let animation = CABasicAnimation(keyPath: "strokeEnd")

override func viewDidLoad() {
    super.viewDidLoad()
    
    graphView.translatesAutoresizingMaskIntoConstraints = true
    graphView.backgroundColor = .blue
    graphView.frame.size = CGSize(width: 100000, height: 250)
    DispatchQueue.main.async {
        self.graphView.layoutIfNeeded()
    }
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    createBezierPath(repeatLoop: 1)
}

@objc func createBezierPath(repeatLoop: Int) {
    let pathsArray = UIBezierPath()
    
    let shapeLayer = CAShapeLayer()
    shapeLayer.fillColor = UIColor.clear.cgColor
    shapeLayer.strokeColor = UIColor.red.cgColor
    shapeLayer.lineWidth = 2.0
    
    for _ in 0..<repeatLoop {
        let path = UIBezierPath()
        path.move(to: CGPoint(x: movingPoint, y: graphView.halfHeight))
        path.addLine(to: CGPoint(x: startInterval, y: graphView.halfHeight))
        
        // P Point Curve
        let pWaveX = startInterval + pWaveWidth
        path.addQuadCurve(to: CGPoint(x: pWaveX, y: graphView.halfHeight), controlPoint: CGPoint(x: pWaveX - 6.0, y: graphView.halfHeight - 30.0))
        
        let gapBetweenPAndQRS: CGFloat = 20.0
        path.addLine(to: CGPoint(x: (pWaveX + gapBetweenPAndQRS), y: graphView.halfHeight))
        
        // QRS
        let qrsStartPoint = pWaveX + gapBetweenPAndQRS
        let qrsComplex = qrsStartPoint + qrsWaveWidth
        let qrsComplexEndPoint = qrsComplex + (qrsWaveWidth / 2)
        path.addLine(to: CGPoint(x: qrsStartPoint + (qrsWaveWidth / 2), y: graphView.halfHeight - 70.0))
        path.addLine(to: CGPoint(x: qrsComplex, y: graphView.halfHeight + 40.0))
        path.addLine(to: CGPoint(x: qrsComplexEndPoint, y: graphView.halfHeight))
        
        let gapBetweenQRSAndTWave: CGFloat = 20.0
        let tWaveStartPoint = qrsComplexEndPoint + gapBetweenQRSAndTWave
        path.addLine(to: CGPoint(x: tWaveStartPoint, y: graphView.halfHeight))
        
        // T Point Curve
        let tWaveEndX = tWaveStartPoint + tWaveWidth
        print(tWaveEndX)
        path.addQuadCurve(to: CGPoint(x: tWaveEndX, y: graphView.halfHeight), controlPoint: CGPoint(x: tWaveEndX - 7.0, y: graphView.halfHeight - 30.0))
        
        movingPoint = tWaveEndX
        startInterval = tWaveEndX + gapBetweenTwoIntervals
        path.addLine(to: CGPoint(x: startInterval, y: graphView.halfHeight))
        pathsArray.append(path)
        
        shapeLayer.path = pathsArray.cgPath
        
        animation.fromValue = 0.0
        animation.duration = Double(repeatLoop) * 2
        shapeLayer.add(animation, forKey: "PathAnimation")
    }
    
    graphView.layer.addSublayer(shapeLayer)
}

@IBAction func btnTap(_ sender: Any) {
    createBezierPath(repeatLoop: 2)
}
}

extension UIView {
var halfHeight: CGFloat {
    return self.frame.size.height / 2.0
}
}

Happy to Help :)

Prateek Varshney
  • 1,114
  • 2
  • 12
  • 29