? ? ? ?前言:現(xiàn)在做金融的越來(lái)越多了,在很多的技術(shù)群中都有人問(wèn)到k線(xiàn)圖怎么去做,有沒(méi)有相關(guān)的框架??jī)赡昵?,我剛?cè)脒@金融公司也是走這條路,但是發(fā)現(xiàn)網(wǎng)上的框架不多,干脆就自己搞一個(gè)出來(lái)。沒(méi)人分享相關(guān)知識(shí),就分享下繪圖心得好了,大家一起探討優(yōu)化。
一、理論知識(shí)
1.用什么去繪制k線(xiàn)圖?
? ? ? ?在移動(dòng)端、前端這邊,繪圖從來(lái)都不是什么大事,包括接下來(lái)的小程序。大多數(shù)都是把相關(guān)的東西提交給系統(tǒng)去做!所以不要怕繪圖!在iOS中,我們經(jīng)常會(huì)用到CoreGraphics上下文渲染,或者用CAShapelayer配合UIBezierPath去做。當(dāng)然,本文不是教你怎么用API,我不會(huì)去侮辱程序員的。。
卡頓:CPU計(jì)算的內(nèi)容太多 來(lái)不及提交新的東西給GPU渲染顯示 它還是弄舊的..?
UIBezierPath:繪制路徑的,繪制出來(lái)的路徑可以給上下文環(huán)境(drawrect系列方法)渲染或者CAShapelayer渲染。上下文環(huán)境對(duì)資源的消耗較大 一不小心就離屏渲染或者各種內(nèi)存問(wèn)題(我也是聽(tīng)來(lái)的 實(shí)際上使用貌似沒(méi)多大消耗 不知道是不是蘋(píng)果改了還是我用對(duì)API了)上下文語(yǔ)法不熟悉的也得看一會(huì)才能好好用,所以我們介紹CAShapelayer為主.
2.坐標(biāo)系?
iOS的坐標(biāo)系由上往下,左往右,這大家都懂。。但是我要說(shuō)的是走勢(shì)圖的坐標(biāo)系!!
繪圖我們肯定要知道x,y坐標(biāo),我們?cè)谕獠總魅脒@個(gè)view的位置大小之后,我們的x坐標(biāo)就基本確定下來(lái)了。比如我用width:320,那么我們可以確定320個(gè),當(dāng)然這個(gè)可以按照需求。一般我走勢(shì)圖數(shù)據(jù)量大的股票期貨都會(huì)默認(rèn)為1,其它的大于一,當(dāng)然,這是可以縮放的。
y坐標(biāo):這個(gè)試圖的高我們一定確定好了。我們現(xiàn)在是要知道這個(gè)高每一點(diǎn)代表的值是多少?比如我現(xiàn)在這個(gè)股票,最高點(diǎn)40,最低點(diǎn)30,現(xiàn)價(jià)32,高度100,那么我們每一點(diǎn)代表0.1,然后這個(gè)是第一個(gè)數(shù)據(jù),所以它的對(duì)應(yīng)坐標(biāo)是(0,20)? 呵呵,答錯(cuò)了!雖然是這個(gè)沒(méi)錯(cuò),但是我們看盤(pán)的時(shí)候是從下往上看的嘛,所以它應(yīng)該是(0,80)的位置(下方往上為20);
3.耗電耗性能?
一開(kāi)始我們的版本是用上下文去渲染顯示的,但是由于考慮不周,被用戶(hù)投訴耗電耗流量非常多。然后就開(kāi)始操刀優(yōu)化了。
說(shuō)點(diǎn)無(wú)關(guān)的:首先解決的事耗流量的問(wèn)題。我們的數(shù)據(jù)都是用TCP去推送的,由于之前的某些原因,數(shù)據(jù)的組合格式過(guò)于浪費(fèi),后來(lái)改為按需拉取,于是就解決了。==! TCP是個(gè)好東西,即時(shí)性高,能主動(dòng)推送數(shù)據(jù)不丟包,應(yīng)用在前臺(tái)還能繞過(guò)APNS,微信的聊天貌似也用TCP+Http。當(dāng)然即時(shí)性不高的可以用輪詢(xún)+設(shè)置超時(shí)90s..你懂的!
那耗電量是什么回事?
我們知道iOS耗電高的 就是不斷的循環(huán)操作、CPU GPU轉(zhuǎn)啊轉(zhuǎn)。。 知道這些就好辦了,優(yōu)化算法減少計(jì)算量,用enumerateObjectsUsingBlock取代forin。。當(dāng)然這些你肯定知道的!但是要解決這個(gè)耗電量得知道它還有什么業(yè)務(wù)操作才行。。 由于期貨股票這東西有時(shí)候撥動(dòng)的非???,一秒幾次,可能這時(shí)候我們已經(jīng)有上千點(diǎn)數(shù)據(jù)準(zhǔn)備去畫(huà)了,一秒算三次,那么CPU過(guò)的非常充實(shí)哦? 我們當(dāng)然不可以這么做。。
那么如何去優(yōu)化它?
我們都知道,iOS的view都是使用組合模式的,基類(lèi)的寫(xiě)好公用的(大概就是模板模式那理論啦),然后繼承這個(gè)類(lèi),然后addSubview..remove..,那么我們可以從中得到什么信息? 那就是分層。。 這也告訴了我們 研究系統(tǒng)API的設(shè)計(jì) 規(guī)范化開(kāi)發(fā)是多么重要..(至少會(huì)減少邏輯bug),順便帶一句,我和別人聊天的時(shí)候都會(huì)問(wèn)block和delegate的應(yīng)用場(chǎng)景。。 大部分人都局限于傳值..這貌似是培訓(xùn)班的套路?? 看看第三方框架或者分析系統(tǒng)框架?
那我們知道這些東西我們差不多可以開(kāi)始動(dòng)工了。
我們知道要做好繪制好走視圖,需要分層,那怎么分? 單一職責(zé)幫助你。
說(shuō)一說(shuō)我們的需求。
1.顯示走勢(shì)圖(廢話(huà))
2.拉動(dòng)顯示其它數(shù)據(jù)
3.十字光標(biāo)滑動(dòng)的時(shí)候 出現(xiàn)左邊或者右邊的對(duì)應(yīng)的點(diǎn)或者蠟燭的詳情(開(kāi)收盤(pán)等) 也有些公司會(huì)用點(diǎn)擊某根蠟燭彈出詳情
4.縮放功能。 暫時(shí)就這么多了 我也不知道還要示范些啥?
二、動(dòng)工
1.確定好需要外界提供什么材料?
想想就有點(diǎn)小激動(dòng)了。。 外界? 當(dāng)然是委托別人去做的啦。。 先寫(xiě)好一個(gè)協(xié)議。我看那個(gè)蠟燭圖怎么和collectionView那么像?。?那我的API也和它差不多就好啦。 好,確定好我需要外界給我東西,就是顯示的個(gè)數(shù),還有現(xiàn)在顯示的范圍。。
設(shè)計(jì)API 就是要簡(jiǎn)單粗暴。。 太難用了自己都不想用啦!
我們知道,現(xiàn)在是在實(shí)現(xiàn)基礎(chǔ)的走視圖 不包含其它的
簡(jiǎn)單些:-(NSInteger)numberOfView:(UIView *)view;
? ? ? ? ? ? ? - (KlineModel *)LineView:(UIView *)view cellAtIndex:(NSInteger)index;
那我們?cè)趺创_定index數(shù)據(jù)?
我們已經(jīng)獲取到了這個(gè)視圖的寬度了,這時(shí)候分情況。1.分時(shí)圖:自定義或者默認(rèn)1;2:k線(xiàn)圖,我們先在上方寫(xiě)好:
static const NSInteger KlineCellSpacing = 2;//cell間隔
static const NSInteger KlineCellWidth = 6;//cell寬度
static const NSInteger CellOffset = 1;//偏移的單位
這個(gè)時(shí)候,我們已經(jīng)清楚的知道 寬度為6 間隔為2,那么一個(gè)單位就是8.. 我有320,那就能畫(huà)40個(gè)咯? 假設(shè)代理給我的個(gè)數(shù)是1200 那我只需要拿1161-1200。 那就回調(diào)它 開(kāi)始獲取數(shù)據(jù)制作k線(xiàn)圖了。
當(dāng)然,這也是需要分兩種情況的,分時(shí)圖太簡(jiǎn)單了 就是一條線(xiàn),那我們拿k線(xiàn)圖來(lái)說(shuō)。 制作原理一樣。
2.開(kāi)始繪圖
繪制基礎(chǔ)顯示圖
假設(shè)這個(gè)時(shí)候 我們已經(jīng)拿到40個(gè)數(shù)據(jù)了。我們先遍歷它,獲得最高最低點(diǎn),獲得每個(gè)點(diǎn)代表的值,當(dāng)然我們要在外面寫(xiě)好willXXX等方法告訴外面 我們知道這個(gè)最高價(jià)最低價(jià)是多少了,順便放出這兩個(gè)只讀屬性(業(yè)務(wù)需求)。這個(gè)時(shí)候 我們有兩種數(shù)據(jù)類(lèi)型,一種是永遠(yuǎn)不變的(至少?zèng)]突破最高最低值的時(shí)候不會(huì)變),一種是會(huì)變動(dòng)的。每次Socket推送過(guò)來(lái)的數(shù)據(jù)我們都要及時(shí)的更新為視圖。那我們開(kāi)始分層了!
先新建一個(gè)CAShapelayer屬性Shapelayer,把剛剛的所有數(shù)據(jù)通過(guò)UIBezierPath 轉(zhuǎn)化為路徑 加載在它這邊,多個(gè)CAShapelayer都加載在這里面。CAShapelayer也是組合模式的。add就好了 設(shè)置好每一個(gè)的顏色各種形 一個(gè)一個(gè)加。(這里說(shuō)的是k線(xiàn)圖 曲線(xiàn)圖就一個(gè)好了 我也是取巧的方法 = =!)
?如果直接加載在這個(gè)Shapelayer屬性,這里就有個(gè)問(wèn)題了..顏色怎么辦?這個(gè)是很重要的,畢竟?jié)q跌一個(gè)點(diǎn)都要錢(qián)啊。。之前我有嘗試過(guò)用漸變?nèi)ソo一個(gè)CAShapelayer染色,但是..慘不忍睹、不忍再提。。 不過(guò)還好 對(duì)性能沒(méi)影響,估計(jì)是底層蘋(píng)果把它給合并的吧?這個(gè)需要點(diǎn)時(shí)間去研究...但是最近又有新項(xiàng)目了 貌似還有接到一個(gè)外包?? 這兩個(gè)問(wèn)題希望大神能賜教 感激不盡。
跑調(diào)了啊 =。=
好吧。畫(huà)完上面的東西了,我們開(kāi)始畫(huà)變的那一層。先定好API 確保API容易用(容不容易去跑下單元測(cè)試),這個(gè)API傳入一個(gè)model。 這個(gè)時(shí)候 我們已經(jīng)有個(gè)Shapelayer,我們拿到它的sublayer數(shù)組 刪掉最后一個(gè)。我們已經(jīng)有每點(diǎn)代表的值了,直接轉(zhuǎn)化顯示就好了。但是,我們不確保它的值不會(huì)我們的最高最低值。那我們比較一下,如果超越了,那就替換剛剛獲得的數(shù)組最后一個(gè)model 然后調(diào)用剛剛的計(jì)算方法 從那一步開(kāi)始重新走一遍,重新計(jì)算。這個(gè)過(guò)程就要看你的API設(shè)計(jì)的是不是職責(zé)單一了。 = =!封裝好每一個(gè)關(guān)聯(lián)的面向過(guò)程實(shí)現(xiàn)吧?
基礎(chǔ)繪圖的介紹就到這里好了。接下來(lái)是拉動(dòng)效果!
拉動(dòng)移動(dòng)顯示其它數(shù)據(jù)
這個(gè)也沒(méi)什么好說(shuō)的。我們繼承的是view對(duì)吧? 加個(gè)手勢(shì)或者截取響應(yīng)鏈。我這邊是用手勢(shì)!剛剛上面我們已經(jīng)有一個(gè)CellOffset的常量了。在手勢(shì)中通過(guò)translationInView獲取的x正負(fù)判斷方向 加減我們的OffsetIndex(一開(kāi)始是從1161開(kāi)始 看上文)。然后移動(dòng)以后 判斷它是不是合法下標(biāo) 重新獲取數(shù)據(jù) 繼續(xù)調(diào)用計(jì)算方法 走流程 。= =! 問(wèn)題:我一直在想,要保持好這些繪圖對(duì)象像CollectionView那樣子 還是直接重新繪制比較省資源。。 這點(diǎn)還是可以繼續(xù)優(yōu)化的。
滑動(dòng)的時(shí)候 聯(lián)動(dòng)左邊或者右邊的對(duì)應(yīng)的點(diǎn)或者蠟燭的詳情(開(kāi)收盤(pán)等)
這個(gè)時(shí)候 我們需要新建一個(gè)ShowTrackingCross,讓外界控制是否顯示十字光標(biāo),還是拉動(dòng)顯示其它數(shù)據(jù).. 這個(gè)時(shí)候 我們可以封裝一個(gè)CALayer類(lèi) 把觸摸點(diǎn)傳進(jìn)一個(gè)方法里面 逆推得出對(duì)應(yīng)數(shù)組的index而得到model,計(jì)算最新價(jià)的y 就可以得出這個(gè)點(diǎn)。在代理里面寫(xiě)個(gè)可選實(shí)現(xiàn)方法把它傳遞出去并繪制這個(gè)十字光標(biāo),再寫(xiě)個(gè)關(guān)閉十字光標(biāo)的方法讓使用者或者自己可以把它移除。
縮放功能
= 。= ?定義好scale屬性,初始化為1。在計(jì)算的時(shí)候參與x,width的計(jì)算。在縮放手勢(shì)的時(shí)候改變它的大小。。 這里要注意在計(jì)算顯示的個(gè)數(shù)也是要參與計(jì)算的= =!
其它細(xì)節(jié)
旋轉(zhuǎn)屏幕等操作需要注意重新setFrame。均線(xiàn) 等其它的也是分層好了,閃電圖直接畫(huà)? 暫時(shí)就先到這里了 大致的說(shuō)一遍。這幾天整理個(gè)demo再?gòu)膶?shí)現(xiàn)說(shuō)起應(yīng)該比較好說(shuō)一點(diǎn)!下次還要探討socket等網(wǎng)絡(luò)編程。希望各位大神指出存在的邏輯繁瑣問(wèn)題 bug 優(yōu)化。只是demo 求大神帶上開(kāi)發(fā)對(duì)應(yīng)組件! 跪謝。