图表封装

图表封装

1. 业务

  • 可滚动的平滑曲线。
  • 选中状态: 位置在中心,点/标题/y轴字体是黑体加粗,其他为未选中状态,颜色字体淡一些。
  • 滚动到中间位置的点显示为选中状态。
  • 可以点击某个点成为选中状态,并滚动到试图中间。

2.思路

  • 滚动视图用UIScrollView横向滚动
  • 使用 CAShapeLayer + UIBezierPath 绘制曲线图表
  • 监听滚动结束后的位置,使最近的点成为选中状态。
  • 监听点击的位置,使最近的点成为选中状态。

3.细节

  • 监听UIScrollView滚动结束

滚动结束的状态有3种,1.滚动减速停止 2.滚动按压停止 3.滚动上下滑动停止。
根据 tracking、dragging、decelerating这3个属性监听停止。

   func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
       // 拖拽停止
       if !decelerate {
           let dragToDragStop = scrollView.isTracking && !scrollView.isDragging && !scrollView.isDecelerating
           if dragToDragStop {
               self.cgo_scrollViewDidEndScroll(scrollView)
           }
       }
   }
   
   func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
       /// 快速滑动自由停止/按压停止
       let scrollToScrollStop = !scrollView.isTracking && !scrollView.isDragging && !scrollView.isDecelerating
       if scrollToScrollStop {
           self.cgo_scrollViewDidEndScroll(scrollView)
       }
   }
  • UIScrollView点击事件

UIScrollView对于touch事件的接收处理原理:UIScrollView重载hitTest 方法,并总会返回itself 。所以所有的touch 事件都会进入到它自己里面去了,所以UIScrollView及其子视图是不响应touchBegan事件的
这里有两种方式,第一种给UIScrollView添加点击手势。在手势结束是处理点击事件

       let tap = UITapGestureRecognizer(target: self, action: #selector(tapEvent(_:)))
       self.chartView.addGestureRecognizer(tap)
   
   @objc func tapEvent(_ gestureRecognizer: UIGestureRecognizer) {
       switch gestureRecognizer.state {
       case .ended:
           do {
               let point = gestureRecognizer.location(in: self.chartView)
               let index = self.calculatePosition(offset: point.x)
               guard let pointIndex = index else {
                   return
               }
               
               debugPrint("ponitIndex === \(pointIndex)")
               self.updateRow(index: pointIndex)
               //        self.lastRow = ponitIndex
               self.adustMidPosition(isDrag: false)
               self.delegate?.chartView(self, didSelectedRowWithIndex: pointIndex)
           }
           break
       default:
           break
       }
   }

第二种是重写touchbegan一系列事件,单独处理点击事件和移动事件,点击事件自己处理,移动事件交给UIScrollView滚动结束处理,但是这里长按也会被统计为移动事件,导致一个小问题。

class CGOChartScrollView: UIScrollView {

   public var isDrag : Bool = false
   public var tapEvent : ((Set<UITouch>)-> Void)?
   override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
       super.touchesBegan(touches, with: event)
   }
   
   override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
       self.isDrag = true
       super.touchesMoved(touches, with: event)
   }
   
   override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
       if self.isDrag {
           super.touchesEnded(touches, with: event)
       } else {
           
           if let event = self.tapEvent {
               event(touches)
           }
       }
       self.isDrag = false
   }
}
  • UIScrollView setContentOffset:animated与contentOffset的区别

setContentOffset有两种方法:setContentOffset:和setContentOffset:animated:
但是两者还是有点差异的:
setContentOffset:animated: 这种方法,无论animated为YES还是NO, 都会等待scrollView的滚动结束以后才会执行,也就是当isDragging和isDecelerating为YES的时候,会等待滚动完成才执行上面的方法。
setContentOffset:这种方法则不受scrollView是否正在滚动的限制。
所以我在滚动结束后和点击事件使用的动画是不同的,需要判断是否是拖拽手势isDrag
设置setContentOffset的动画

       if isDrag {
           self.chartView.setContentOffset(CGPoint(x: m_offset, y: 0), animated: true)
       } else {
           UIView.animate(withDuration: 0.3, delay: 0, options: UIView.AnimationOptions.curveEaseInOut, animations: {
               self.chartView.contentOffset = CGPoint(x: m_offset, y: 0)
           }, completion: nil)
       }
  • 滑动或者点击后选中状态的绘制。
  • 第一次进入时只绘制可见部分,后面滚动到什么位置就绘制到哪里。

4.接口

  • 数据接口设计

避免数据的耦合,可以使用多种方式,比如UITableVIew的dataSource,我这里使用强制类型CGOChartData

class CGOChartDataSet : NSObject {
   public var title : String = ""
   public var prefix : String = "R$"
   public var yAixsText : String = ""
   public var isSelected : Bool = false
   
}

class CGOChartData : NSObject {
   public var dataSet : CGOChartDataSet?
   public var point : CGPoint = CGPoint.zero
}

dataSet是对数据的设置
使用的时候需要进行转换

       var data : Array<CGOChartData> = Array<CGOChartData>()
       for (index,value) in a.enumerated() {
           let d = CGOChartData()
           let set = CGOChartDataSet()
           d.point = self.chartView.getPoint(index: index, value: value.consumption, count: 6)
           set.title = "\(value.consumption)"
           set.isSelected = value.isSelected
           set.yAixsText = "\(value.month)"
           d.dataSet = set
           data.append(d)
       }
       self.chartView.points = data
  • 对外接口设计

选中状态完成时的接口,这里包括点击选中和滚动后选中

// 接口
protocol CGOLineChartViewProtocol : NSObjectProtocol {
   // 选中结束事件
   func chartView(_ chartView: CGOLineChartView, didSelectedRowWithIndex: NSInteger) -> Void
   
}

5.优化

6.性能

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容