如何解决用动态值绘制折线图objective c / swift
我正在实现线图,如附加的视频和图像所示,每 1 秒在数组中添加动态值,主要是我希望使用附加视频中所示的动画绘制图形。
到目前为止,我已经尝试使用 https://github.com/Boris-Em/BEMSimpleLineGraph,但没有得到所需的结果。
OUTPUT REQUIRED VIDEO LINK 此参考视频来自我的 android 应用。
CURRENT OUTPUT VIDEO LINK 这是我使用 BEMSimpleLineGraph 得到的输出
注意:- 正如我们在当前输出的视频中看到的那样,没有参考视频的平滑度和动画,因此需要实现相同的
需要获得完全如图所示的平滑度和曲线HERE.!!
此外,我在研究期间尝试构建用于从链接中获取引用的图形的自定义文件,以下是我尝试过的代码
class GraphCustom: UIView {
// var data: [CGFloat] = [2,6,12,4,5,7,3] {
// didSet {
// setNeedsDisplay()
// }
// }
@objc var dynamicData : [CGFloat] = [0]{
didSet {
setNeedsDisplay()
}
}
let shapeLayer = CAShapeLayer()
var fromValue = CGPoint()
var toValue = CGPoint()
func coordYFor(index: Int) -> CGFloat {
return bounds.height - bounds.height * dynamicData[index] / (dynamicData.max() ?? 0)
}
@objc override func draw(_ rect: CGRect) {
let path = quadCurvedPath()
// UIColor.black.setStroke()
// path.lineWidth = 1
// path.stroke()
// let animation = CABasicAnimation(keyPath: "strokeEnd")
// let timing = CAMediaTimingFunction()
// animation.duration = 10.0
// animation.fromValue = 0
// animation.toValue = 1
// self.layer.add(animation,forKey: "strokeAnim")
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 2.0
shapeLayer.lineCap = kCALineCapRound
self.layer.addSublayer(shapeLayer)
}
@objc func drawLine(value:CGFloat){
dynamicData.append(value)
if (dynamicData.count > 3){
dynamicData.remove(at: 0)
}
print("dynamicData >>>>>>>",dynamicData)
// let path = quadCurvedPath()
//
// let animation = CABasicAnimation(keyPath: "strokeEnd")
//
//// animation.fromValue = 0.0
//// animation.byValue = 1.0
// animation.duration = 2.0
//
// animation.fillMode = kCAFillModeForwards
// animation.isRemovedOnCompletion = false
//
// shapeLayer.add(animation,forKey: "drawLineAnimation")
// UIColor.black.setStroke()
// path.lineWidth = 1
// path.stroke()
}
func quadCurvedPath() -> UIBezierPath {
let path = UIBezierPath()
let step = bounds.width / CGFloat(dynamicData.count - 1)
print("Indccccc>>>>",dynamicData.count)
var p1 = CGPoint(x: 0,y: coordYFor(index:dynamicData.count - 1 ))
path.move(to: p1)
// drawPoint(point: p1,color: UIColor.red,radius: 3)
if (dynamicData.count == 2) {
path.addLine(to: CGPoint(x: step,y: coordYFor(index: 1)))
return path
}
var oldControlP: CGPoint?
for i in 0..<dynamicData.count {
let p2 = CGPoint(x: step * CGFloat(i),y: coordYFor(index: i))
let mid = midPoint(p1: p1,p2: p2)
path.addQuadCurve(to: mid,controlPoint: controlPoint(p1: mid,p2: p1))
path.addQuadCurve(to: p2,p2: p2))
p1 = p2
}
/* for i in 1..<dynamicData.count {
print(">>>>>>>>>>",i)
let p2 = CGPoint(x: step * CGFloat(i),y: coordYFor(index: i))
// drawPoint(point: p2,radius: 3)
var p3: CGPoint?
if i < dynamicData.count - 1 {
p3 = CGPoint(x: step * CGFloat(i + 1),y: coordYFor(index: i + 1))
}
let newControlP = controlPointForPoints(p1: p1,p2: p2,next: p3)
// print(" >>>>>>>>>>>>>>>>",p2,oldControlP ?? p1,newControlP ?? p2)
path.addCurve(to: p2,controlPoint1: oldControlP ?? p1,controlPoint2: newControlP ?? p2)
fromValue = oldControlP ?? p1
toValue = newControlP ?? p2
p1 = p2
oldControlP = antipodalFor(point: newControlP,center: p2)
}*/
return path;
}
func controlPoint(p1: CGPoint,p2: CGPoint) -> CGPoint {
var controlPoint = midPoint(p1: p1,p2: p2)
let diffY = abs(p2.y - controlPoint.y)
if p1.y < p2.y {
controlPoint.y += diffY
} else if p1.y > p2.y {
controlPoint.y -= diffY
}
return controlPoint
}
func midPoint(p1: CGPoint,p2: CGPoint) -> CGPoint {
return CGPoint(x: (p1.x + p2.x) / 2,y: (p1.y + p2.y) / 2)
}
/// located on the opposite side from the center point
func antipodalFor(point: CGPoint?,center: CGPoint?) -> CGPoint? {
guard let p1 = point,let center = center else {
return nil
}
let newX = 2 * center.x - p1.x
let diffY = abs(p1.y - center.y)
let newY = center.y + diffY * (p1.y < center.y ? 1 : -1)
return CGPoint(x: newX,y: newY)
}
/// halfway of two points
func midPointForPoints(p1: CGPoint,p2: CGPoint) -> CGPoint {
return CGPoint(x: (p1.x + p2.x) / 2,y: (p1.y + p2.y) / 2);
}
/// Find controlPoint2 for addCurve
/// - Parameters:
/// - p1: first point of curve
/// - p2: second point of curve whose control point we are looking for
/// - next: predicted next point which will use antipodal control point for finded
func controlPointForPoints(p1: CGPoint,p2: CGPoint,next p3: CGPoint?) -> CGPoint? {
guard let p3 = p3 else {
return nil
}
let leftMidPoint = midPointForPoints(p1: p1,p2: p2)
let rightMidPoint = midPointForPoints(p1: p2,p2: p3)
var controlPoint = midPointForPoints(p1: leftMidPoint,p2: antipodalFor(point: rightMidPoint,center: p2)!)
if p1.y.between(a: p2.y,b: controlPoint.y) {
controlPoint.y = p1.y
} else if p2.y.between(a: p1.y,b: controlPoint.y) {
controlPoint.y = p2.y
}
let imaginContol = antipodalFor(point: controlPoint,center: p2)!
if p2.y.between(a: p3.y,b: imaginContol.y) {
controlPoint.y = p2.y
}
if p3.y.between(a: p2.y,b: imaginContol.y) {
let diffY = abs(p2.y - p3.y)
controlPoint.y = p2.y + diffY * (p3.y < p2.y ? 1 : -1)
}
// make lines easier
controlPoint.x += (p2.x - p1.x) * 0.1
return controlPoint
}
func drawPoint(point: CGPoint,color: UIColor,radius: CGFloat) {
let ovalPath = UIBezierPath(ovalIn: CGRect(x: point.x - radius,y: point.y - radius,width: radius * 2,height: radius * 2))
color.setFill()
ovalPath.fill()
}
}
extension CGFloat {
func between(a: CGFloat,b: CGFloat) -> Bool {
return self >= Swift.min(a,b) && self <= Swift.max(a,b)
}
}
很长时间以来,我一直试图获得所需的输出,并且几乎尝试了 stackoverflow 上的所有可用链接。
任何帮助都会很棒..!!
谢谢。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。