用动态值绘制折线图objective c / swift

如何解决用动态值绘制折线图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 上的所有可用链接。

任何帮助都会很棒..!!

谢谢。

image

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res