日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

swift:打造你自己的折线图

發(fā)布時(shí)間:2023/12/18 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 swift:打造你自己的折线图 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

看到蘋果Health里的折線圖了嗎。我們就是要打造一個(gè)這樣的折線圖。沒看過的請(qǐng)看下圖。

我們的主題在于折線圖本身。其他的包括步數(shù)、日平均值等描述類的內(nèi)容這里就不涉及了。

?

首先觀察,這個(gè)圖種包含些什么組成部分。?這個(gè)太明顯都看見了。還有每個(gè)節(jié)點(diǎn)的小圓圈,還有折線圖里從上到下的漸變。這里是白色的從上到下逐漸透明的效果。還有一條虛線。這個(gè)暫時(shí)先不考慮了。你能繪制出來(lái)最下面的x軸標(biāo)尺,繪制個(gè)虛線還不是小菜?

為什么說是繪制呢,因?yàn)轱@然我們不想用一個(gè)UIView把像素設(shè)置為1,背景色設(shè)置為UIColor.whiteColor(),然后設(shè)置View 的傾斜度的方式來(lái)堆砌這個(gè)line chat。首先必須嚴(yán)重的鄙視這種做法。在開發(fā)中不能光是把各種UIButton、UILabel什么的設(shè)定好了frame就網(wǎng)上沒完沒了的堆。或者更有 甚者直接拖動(dòng)這些控件到Storyboard上。擺個(gè)位置,設(shè)置個(gè)寬和高別的就完全不管了。autolayout什么的一概不問,使用了 storyboard也適配不了多分辨率。這樣的結(jié)果是誰(shuí)維護(hù)代碼誰(shuí)遭殃。

正確的做法是提升代碼。有多個(gè)地方都用到同樣的組合控件的時(shí)候,比如多選框、單選框,就自定義一個(gè)。這樣,至少可以達(dá)到一改全改的效果。代碼維護(hù)簡(jiǎn) 單了很多。同時(shí)需要考慮效率的問題。比如我們的line chart,就使用Core Graphics和QuartzCore框架中的CAShapeLayer繪制。這樣執(zhí)行效率明顯比堆砌UIView的方法效率高--占用資源少,執(zhí)行 快。

看看CALayer的定義:

class CALayer : NSObject, NSCoding, CAMediaTiming

再看看UIView的定義:

class UIView : UIResponder, NSCoding, UIAppearance, NSObjectProtocol, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace

你就應(yīng)該知道為什么完全不能用UIView來(lái)堆砌這個(gè)圖了。

言歸正傳!畫線可以用Core Graphics一點(diǎn)點(diǎn)的畫,也可以用CALayer來(lái)話,準(zhǔn)確的說是CAShapeLayer更方便,所以我們用CAShapeLayer來(lái)畫線。用 CAShapeLayer畫線灰常之簡(jiǎn)單。總的來(lái)說就是設(shè)定路線(Path),然后把這個(gè)路線賦值給這個(gè)layer畫線就完成了。比如,初始化一條貝塞爾 曲線,然后指定好center point和半徑,起始角度和結(jié)束角度,然后“BANG”。“BANG”是一個(gè)象聲詞,龍珠里很多。指定你的CAShapeLayer實(shí)例的path屬性 值為這個(gè)path。此處略去一堆什么給你的view.layer.addsublayer什么的細(xì)節(jié)。運(yùn)行后你就會(huì)看到一個(gè)從起始角度到結(jié)束角度的一個(gè)半 圓。

運(yùn)行起來(lái)之后,你會(huì)看到這個(gè)半圓和你需要的起始角度、結(jié)束角度差很多。所以,還是畫一個(gè)正圓比較容易一些。尤其現(xiàn)在我們才剛剛開始接觸這個(gè)神秘的東 東。等下還有更神秘的。。。要畫正圓只要指定起始角度為0(這里需要嚴(yán)重說明一下,角度都是弧度制的,比如,π、2π什么的)。結(jié)束角度為2π,也就是(M_PI * 2)。半徑隨便,圓心最好設(shè)定在屏幕的中心,也就是:

UIScreen.mainScreen().bounds.height /?2和UIScreen.mainScreen().bounds.width /?2。這樣就是在屏幕中心點(diǎn),以你給定的值為半徑畫了一個(gè)圓圈。效果如圖:

給的貝塞爾曲線是這樣的:

UIBezierPath(arcCenter: centerPoint, radius: CGRectGetWidth(bounds) / 2 - 30.0, startAngle: 0, endAngle: CGFloat(M_PI * 2.0), clockwise: true).CGPath

這里需要注意的是一定要在最后調(diào)用屬性CGPath,這個(gè)才是CAShapeLayer可以接受的Path的類型。直接賦值是會(huì)報(bào)錯(cuò)的。在貝塞爾曲 線初始化的過程中角度值需要使用CGFloat類型。M_PI是Double類型的。這里需要類型轉(zhuǎn)換一下。否則報(bào)錯(cuò)會(huì)報(bào)在radius的身上,但是起始 是角度的類型問題。

圓是畫出來(lái)了,但是我們要繪制的是line chart,是直線。該如何解決呢。這里就需要說明一下繪制線的一般感性認(rèn)識(shí)。首先CAShapeLayer需要知道繪制的起始點(diǎn)在哪里,其次,從哪一點(diǎn) 到哪一點(diǎn)繪制一條線。對(duì)于圓的貝塞爾曲線來(lái)說自然是從角度為0的,半徑長(zhǎng)度和圓心來(lái)開始畫線,線一直延續(xù)到結(jié)束角度2π(PI)。對(duì)于一條直線就簡(jiǎn)單多 了。起點(diǎn)是指定的一個(gè)點(diǎn)。然后,添加一條線到另一個(gè)點(diǎn)。來(lái)看看如何具體的用代碼畫一條線。

var path = CGPathCreateMutable()var x = UIScreen.mainScreen().bounds.width / 2, y = UIScreen.mainScreen().bounds.height / 5CGPathMoveToPoint(path, nil, 0, y * 2)

? ? ? ? ? CGPathAddLineToPoint(path, nil, 0, 0)

? ? ? ? ??CGPathAddLineToPoint(path, nil, x - kRadiusLength, 0)

CGPathAddLineToPoint(path, nil, bounds.size.width, bounds.size.height)progressLayer.path = path

線就是這么畫出來(lái)的。有線了以后就需要考慮另一個(gè)問題了,線下面的漸變色。這個(gè)就需要用到另一種Layer:CAGradientLayer。CAGradientLayer有一個(gè)屬性可以做到這一點(diǎn),這個(gè)屬性就是colors。給這個(gè)屬性多少顏色,CAGradientLayer就會(huì)出現(xiàn)多少?gòu)囊粋€(gè)顏色到另一個(gè)顏色的漸變。注意一點(diǎn),這里需要的顏色都是UIColor.yellowColor().CGColor。看到這個(gè)CGColor了嗎?一定要這個(gè)顏色才行。否則,不報(bào)錯(cuò),也不顯示任何的顏色!

代碼:

var gradientLayer2 = CAGradientLayer()gradientLayer2.startPoint = CGPointMake(0.5, 1.0)gradientLayer2.endPoint = CGPointMake(0.5, 0.0)gradientLayer2.frame = CGRectMake(0, 0, bounds.size.width, bounds.size.height) gradientLayer2.colors = [UIColor.yellowColor().CGColor, UIColor.blueColor().CGColor, UIColor.greenColor().CGColor] self.view.layer.addSublayer(gradientLayer2)

這效果就出來(lái)了:

到這里你應(yīng)該就明白了。圖一種的白色到透明的漸變其實(shí)就是不同alpha的白色賦值給了colors屬性。?

gradientLayer2.colors = [UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.0).CGColor,UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.5).CGColor, UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.8).CGColor]

看效果,白色從上到下的漸變填充已經(jīng)出來(lái)了。畫線前面已經(jīng)講過。現(xiàn)在的問題就是讓這個(gè)填充按照畫得線剪裁。這個(gè)非常簡(jiǎn)單。

我們來(lái)給上面的CAShapeLayer這樣的一個(gè)路線:

var path = CGPathCreateMutable()CGPathMoveToPoint(path, nil, 0, UIScreen.mainScreen().bounds.height)CGPathAddLineToPoint(path, nil, 0, 0)CGPathAddLineToPoint(path, nil, x - kRadiusLength, 0)CGPathAddLineToPoint(path, nil, bounds.size.width, bounds.size.height / 2)

然后,就讓CAGradientLayer的mask屬性為這個(gè)CAShapeLayer。

gradientLayer.mask = progressLayer

這樣一來(lái)。效果就出來(lái)了。

但是。。仔細(xì)一個(gè),填充的漸變白色圖是有了,那么線呢?白色的線沒有。CAShapeLayer的線最終都只是成為CAGradientLayer的剪裁線。要解決這個(gè)問題就要上下面的重頭戲了。

為了解決這個(gè)問題,我們不得不祭出Core Graphics神器了。總體的構(gòu)造思路是在Controller中添加一個(gè)View,在這個(gè)View中使用Core Graphics來(lái)畫線,之后在上面添加我們上文說到的兩個(gè)Layer。也就是下面畫線,然后用Layer來(lái)完成漸變色的填充和對(duì)這個(gè)填充色的剪裁。

Core Graphics畫線比CALayer還是麻煩一些的,但是思路總體上一致。也是把畫筆放到起始點(diǎn)(在哪里開始畫線)。之后也是從哪里到哪里畫線。總體來(lái)說,畫線的思路就是這樣。

首先,需要在Core Graphics中鋪上畫布:

var context = UIGraphicsGetCurrentContext()

2. 指定線的顏色和線的寬度:

CGContextSetStrokeColorWithColor(context, UIColor.whiteColor().CGColor) CGContextSetLineWidth(context, 1.0)

3. 開始畫線:

CGContextMoveToPoint(context, kBottomMargin, CGRectGetHeight(rect) - kBottomMargin) CGContextAddLineToPoint(context, CGRectGetWidth(rect) - kBottomMargin, CGRectGetHeight(rect) - kBottomMargin)

這里必須補(bǔ)充一點(diǎn)。在畫線的時(shí)候,我們需要一些列的點(diǎn)坐標(biāo)。暫時(shí),只是用模擬的方式實(shí)現(xiàn)。var x = calculateX(0)var y = calculateY(0)就是第一個(gè)點(diǎn)得x,y坐標(biāo)的計(jì)算方法。具體的代碼在后面。這些給定的點(diǎn)需要映射到你的畫布的坐標(biāo)系中。calculateX、Y就是做這個(gè)映射的。雖然省略了一些步驟。但是你應(yīng)該可以從初中的數(shù)學(xué)基礎(chǔ)中明白這個(gè)是怎么回事的,所以此處只做解釋其他省略。

func calculateX(i: Int) -> CGFloat {var x = kBottomMargin + CGFloat(i) * kUnitLabelWidth!return x}

kBottomMargin是x點(diǎn)在左側(cè)的一個(gè)margin。只是展示需要,不用關(guān)心。?CGFloat(i) * kUnitLabelWidth!,i是第幾個(gè)點(diǎn),也就是x軸上的index。kUnitLabelWidth!是x軸上兩點(diǎn)之間的距離,至于感嘆號(hào)就不多解釋了,那個(gè)是swift的基礎(chǔ)。

?

func calculateY(i: Int) -> CGFloat {var y: CGFloat = 0switch(i){case 0:y = kTotalYValue! * 0.5 break case 1: y = kTotalYValue! * 0.3 break case 2: y = kTotalYValue! * 0.7 break case 3: y = kTotalYValue! * 0.7 break case 4: y = kTotalYValue! * 0.2 break case 5: y = kTotalYValue! * 0.8 break default: y = 0 break } return y }

這里主要計(jì)算,每個(gè)x點(diǎn)對(duì)應(yīng)的y點(diǎn)(這里就摸你了y值對(duì)應(yīng)在畫布坐標(biāo)系的方法)。

有了以上的只是就可以畫出折線圖了。具體的方法如下:

override func drawRect(rect: CGRect) {println("drawRect")var context = UIGraphicsGetCurrentContext()// CGContextSetStrokeColorWithColor(context, UIColor.blueColor().CGColor) // CGContextSetLineWidth(context, 4.0) // CGContextMoveToPoint(context, kBottomMargin, kBottomMargin) // CGContextAddLineToPoint(context, CGRectGetWidth(rect) - kBottomMargin, CGRectGetHeight(rect) - kBottomMargin) // CGContextStrokePath(context) CGContextSetStrokeColorWithColor(context, UIColor.whiteColor().CGColor) CGContextSetLineWidth(context, 1.0) CGContextMoveToPoint(context, kBottomMargin, CGRectGetHeight(rect) - kBottomMargin) CGContextAddLineToPoint(context, CGRectGetWidth(rect) - kBottomMargin, CGRectGetHeight(rect) - kBottomMargin) // CGContextStrokePath(context) CGContextSetFillColorWithColor(context, UIColor.orangeColor().CGColor) var x = calculateX(0) var y = calculateY(0) var prePoint: CGPoint = CGPointMake(x, y) for var index = 0; index < 6; index++ { var x = calculateX(index) var y = calculateY(index) var textY = CGRectGetHeight(rect) - kBottomMargin + 3 CGContextMoveToPoint(context, x, CGRectGetHeight(rect) - kBottomMargin) CGContextAddLineToPoint(context, x, CGRectGetHeight(rect) - kBottomMargin + kUnitLabelHeight) var labelString = NSString(string: "\(kBaseLabelString) \(index)") labelString.drawAtPoint(CGPointMake(x + kUnitLabelHeight, textY), withAttributes: [NSFontAttributeName: kLabelFont, NSForegroundColorAttributeName: kLabelFontColor]) CGContextStrokePath(context) CGContextMoveToPoint(context, x, y) // CGContextSetLineWidth(context, 2.0) var path = UIBezierPath(arcCenter: CGPointMake(x, y), radius: kCircleRadiusLength, startAngle: CGFloat(0.0) , endAngle: CGFloat(2 * M_PI), clockwise: true) CGContextAddPath(context, path.CGPath) // CGContextFillPath(context) CGContextStrokePath(context) // var offset: CGFloat = kCircleRadiusLength * CGFloat(sin(M_PI_4)) var offset = calculateOffset(prePoint.x, prePoint.y, x, y, kCircleRadiusLength) if prePoint.x != x /*&& prePoint.y != y*/ { if y > prePoint.y { CGContextMoveToPoint(context, prePoint.x + offset.offsetX, prePoint.y + offset.offsetY) CGContextAddLineToPoint(context, x - offset.offsetX, y - offset.offsetY) } else if y < prePoint.y { CGContextMoveToPoint(context, prePoint.x + offset.offsetX, prePoint.y - offset.offsetY) CGContextAddLineToPoint(context, x - offset.offsetX, y + offset.offsetY) } else{ CGContextMoveToPoint(context, prePoint.x + offset.offsetX, prePoint.y) CGContextAddLineToPoint(context, x - offset.offsetX, y) } CGContextStrokePath(context) prePoint = CGPointMake(x, y) } } // CGContextMoveToPoint(context, x, y) CGContextSetLineWidth(context, 3) CGContextSetStrokeColorWithColor(context, UIColor.greenColor().CGColor) CGContextSetFillColorWithColor(context, UIColor.blueColor().CGColor) var circleRect = CGRectMake(x, y, 15, 15) circleRect = CGRectInset(circleRect, 3, 3) CGContextFillEllipseInRect(context, circleRect) CGContextStrokeEllipseInRect(context, circleRect) }

這一段代碼:

var path = UIBezierPath(arcCenter: CGPointMake(x, y), radius: kCircleRadiusLength, startAngle: CGFloat(0.0), endAngle: CGFloat(2 * M_PI), clockwise: true)CGContextAddPath(context, path.CGPath)

就是用來(lái)在各條線之間畫圓圈的。

以幾乎略有不同的算法可以在calayer上繪制出CAGradientLayer的mask路線。也就是在core graphics里畫得白線和在紙上鋪上去的mask以后的gradient layer可以嚴(yán)絲合縫的組合在一起。這是看起來(lái)才能和蘋果的health app一樣的效果。這里需要說明,在添加了圓圈之后,每次畫線的時(shí)候需要考慮要把線縮短。如果直接按照原來(lái)的方式的話,會(huì)優(yōu)先穿過圓圈。

轉(zhuǎn)載于:https://www.cnblogs.com/Free-Thinker/p/4946357.html

總結(jié)

以上是生活随笔為你收集整理的swift:打造你自己的折线图的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。