[譯] OS X 和 iOS 的測繪框架 - Core Plot 入門教程

leilei03 8年前發布 | 29K 次閱讀 iOS開發 移動開發

注意 :本篇教程已被 Attila Hegedüs 更新,可適用于 iOS 9 和 Swift 2.2。原始教程出自教程組成員 Steve Baranski。

如果你曾經想在自己的 app 中引入圖表或圖形,那么你應該已經考慮過下面兩種選項:

  1. 自己寫。 通過使用 Core Graphics 或者 Quartz 這樣的框架編寫全部的繪制代碼。然而,這顯然要花費大量的功夫。

  2. 買一個! 購買一個像 ShinobiControls 這樣的商業型框架。這或許可以節省你的時間,但就要花錢啦。

但是如果你不想花費時間和精力從零開始寫(代碼),也不想花那么多錢,該怎么辦呢?這時候第三個選項就派上用場了:使用開源庫 Core Plot

Core Plot 是一個2D繪制庫,適用于 iOS,Mac OS X 和 tvOS。它使用了像 Quartz 和 Core Animation 這樣的蘋果應用框架,同時有著全面的測試覆蓋,而且是遵照BSD這個比較寬松的許可證進行發布的。

在這個教程中,你將學習到如何使用 Core Plot 來創建餅圖和柱狀圖,同時還會實現一些很酷的圖表交互!

開始之前,你需要安裝好 Xcode 7.3 ,同時對 SwiftInterface Builderstoryboards 有所了解。如果你對這些主題知之甚少,那么你應該在繼續閱讀本教程之前先學習一下我們其他的一些 教程

本教程同時還使用了 CocoaPods 去安裝一些第三方的依賴庫。如果你從來沒使用過 CocoaPods 的話,那你還應該閱讀一下我們關于它的 教程

入門

在本教程中,你將創建一個在一定時間間隔內顯示貨幣匯率(情況)的 App。從 這里 下載本教程的入門項目,把它解壓縮后打開 SwiftRates.xcworkspace

項目的關鍵類在 App 這個文件夾和它的子文件夾下,它們包括了:

  • DataStore.swift 這是一個從 Fixer.io 請求貨幣匯率數據的幫助類。

  • Rate.swift 這是一個模型,表示給定日期里的貨幣匯率。

  • Currency.swift 這是一個表示貨幣類型的模型。支持的貨幣類型定義在 Resources/Currencies.plist 里。

  • MenuViewController.swift 這是一個app啟動后展示的第一個視圖控制器。它讓用戶選擇一個貨幣作為基準然后再選兩個對照。

  • HostViewController.swift 這是一個容器視圖控制器,基于它的分段選項選中狀態去控制展示 PieChartViewController 或者 BarGraphViewController 的內容。它還會去檢查從 DataStore 請求來的匯率數據,因為它們也將在這個視圖控制器里展現。

  • PieChartViewController.swift 這個控制器將用餅圖的形式展示一個給定日期里的匯率。當然你首先要實現它!

  • BarGraphViewController.swift 這個控制器將以柱狀圖的形式展示幾天的匯率。當你掌握繪制餅圖的方法后,這個圖簡直小菜一碟!(看到我做的事情了嗎?拜托,這真的有點意思!);]

構建并運行看看這個教程入門項目實際展示。

點選 Get Rates 導航去到 HostViewController 控制的視圖然后可以切換分段選項。這個 app 確實還沒有實現太多功能...;]

是時候用 Core Plot 開始真正的繪制了!

安裝 Core Plot

首先你需要安裝 Core Plot,最簡單的方式是通過 CocoaPods 安裝。

把下面這行代碼添加進你的 Podfile 文件, pod 'SwiftDate' 這行的后面:

pod 'CorePlot', '~> 2.1'

打開 Terminal (終端), cd 進入你的項目根目錄,然后運行 pod install 。

安裝完成后,構建項目。

沒報錯吧?很好,現在你可以隨便使用 Core Plot 啦,感謝 CocoaPods。:]

如果你遇到了任何報錯,可以嘗試通過 sudo gem install cocoapods 更新一下 CocoaPods 然后再次運行 pod install 。

創建餅圖

打開 PieChartViewController.swift 并添加下面這行引入:

import CorePlot

接著,添加下面這個屬性:

@IBOutlet weak var hostView: CPTGraphHostingView!

CPTGraphHostingView 負責“托管”一個圖表或圖形。你可以把它想象成一個“圖形容器”。

然后,把下面這個類擴展添加到文件結尾的花括號之后:

extension PieChartViewController: CPTPieChartDataSource, CPTPieChartDelegate {

  func numberOfRecordsForPlot(plot: CPTPlot) -> UInt {
    return 0
  }

  func numberForPlot(plot: CPTPlot, field fieldEnum: UInt, recordIndex idx: UInt) -> AnyObject? {
    return 0
  }

  func dataLabelForPlot(plot: CPTPlot, recordIndex idx: UInt) -> CPTLayer? {
    return nil
  }

  func sliceFillForPieChart(pieChart: CPTPieChart, recordIndex idx: UInt) -> CPTFill? {
    return nil
  }

  func legendTitleForPieChart(pieChart: CPTPieChart, recordIndex idx: UInt) -> String? {
    return nil
  }
}

你將通過 CPTPieChartDataSource 為一個 Core Plot 圖表提供數據,同時你會通過 CPTPieChartDelegate 得到用戶交互的所有事件。隨著教程遞進,你將填滿這些方法。

建立圖表托管視圖

繼續往下,打開 Main.storyboard 然后選擇 PieChartViewController 窗口。

在這個視圖上拖出一個新的 UIView ,然后把它的類更改成 CPTGraphHostingView ,并將它連接到 hostView 。

對這個視圖的每個方向添加約束讓撐滿父視圖,并確認沒有設置外邊距的約束:

設置一個你喜歡的背景色。我使用了透明度為92%的灰度顏色。

現在回到 PieChartViewController.swift ,在 viewDidLoad() 后面添加下面的方法:

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()
  initPlot()
}

func initPlot() {
  configureHostView()
  configureGraph()
  configureChart()
  configureLegend()
}

func configureHostView() {
}

func configureGraph() {
}

func configureChart() {
}

func configureLegend() {
}

這樣子就正好在子視圖渲染好后設置了繪制策略。這里是你最早為視圖設置框架大小的地方,接下來你將需要配置繪制策略。

initPlot() 里的每個方法都代表了一個設置繪制策略的階段。這樣子可以讓代碼保持其可維護性。

把下面這行添加進 configureHostView() :

hostView.allowPinchScaling = false

這行代碼將對餅圖禁用手勢捏合縮放,它決定了托管視圖對捏合手勢是否會有反應。

接下來你需要添加一個圖表到 hostView 。添加下面的代碼到 configureGraph() 里吧:

// 1 - Create and configure the graph
let graph = CPTXYGraph(frame: hostView.bounds)
hostView.hostedGraph = graph
graph.paddingLeft = 0.0
graph.paddingTop = 0.0
graph.paddingRight = 0.0
graph.paddingBottom = 0.0
graph.axisSet = nil

// 2 - Create text style
let textStyle: CPTMutableTextStyle = CPTMutableTextStyle()
textStyle.color = CPTColor.blackColor()
textStyle.fontName = "HelveticaNeue-Bold"
textStyle.fontSize = 16.0
textStyle.textAlignment = .Center

// 3 - Set graph title and text style
graph.title = "\(base.name) exchange rates\n\(rate.date)"
graph.titleTextStyle = textStyle
graph.titlePlotAreaFrameAnchor = CPTRectAnchor.Top

下面對每個部分的代碼進行分解:

  1. 首先你創建了一個 CPTXYGraph 的實例并指定它作為 hostView 的 hostedGraph 。這就將圖表和托管視圖聯系起來了。

    這個 `CPTGraph` 包括了你所看到的標準圖表或圖形的全部東西:邊,標題,繪制相關數據,軸和圖例。
    
    默認情況下,`CPTXYGraph` 每個方向都有一個`20`的內邊距。從我們這個項目來看這樣并不好,所以你可以顯式地將每個方向的內邊距設置為`0`。
  2. 接下來就是通過創建和配置一個 CPTMutableTextStyle 實例來設置該圖標標題的文本樣式。

  3. 最后,就是給你剛剛創建的圖表實例設置標題和其樣式。同樣你還需要指定標題錨點為該視圖的上邊界。

構建并運行app,你應該就可以看到這個圖表的標題展示在屏幕上了:

繪制餅圖

標題看起來不錯,但你知道接下來什么會更棒嗎?確確實實地看到餅圖!

將下面的代碼添加進 configureChart() :

// 1 - Get a reference to the graph
let graph = hostView.hostedGraph!

// 2 - Create the chart
let pieChart = CPTPieChart()
pieChart.delegate = self
pieChart.dataSource = self
pieChart.pieRadius = (min(hostView.bounds.size.width, hostView.bounds.size.height) * 0.7) / 2
pieChart.identifier = graph.title
pieChart.startAngle = CGFloat(M_PI_4)
pieChart.sliceDirection = .Clockwise
pieChart.labelOffset = -0.6 * pieChart.pieRadius

// 3 - Configure border style
let borderStyle = CPTMutableLineStyle()
borderStyle.lineColor = CPTColor.whiteColor()
borderStyle.lineWidth = 2.0
pieChart.borderLineStyle = borderStyle

// 4 - Configure text style
let textStyle = CPTMutableTextStyle()
textStyle.color = CPTColor.whiteColor()
textStyle.textAlignment = .Center
pieChart.labelTextStyle = textStyle

// 3 - Add chart to graph
graph.addPlot(pieChart)

下面看看這段代碼做了什么:

  1. 首先獲取了剛剛創建的圖表的引用。

  2. 然后實例化一個 CPTPieChart ,將它的代理和數據源設置成這個視圖控制器本身,并配置它的一些外觀屬性。

  3. 接著配置這個圖表的邊框樣式。

  4. 配置它的文本樣式。

  5. 最后,將這個餅圖添加進剛剛引用的圖表里。

如果現在重新構建并運行 app,你將看不到任何變化...因為你還需要實現這個餅圖的代理和數據源。

首先,用下面這段替代了現在的 numberOfRecordsForPlot(_:) 方法:

func numberOfRecordsForPlot(plot: CPTPlot) -> UInt {
  return UInt(symbols.count) ?? 0
}

這個方法決定了有多少塊(部分)顯示在餅狀圖上,它將為每一個標記顯示一塊(部分)。

接下來,用下面這段替換掉 numberForPlot(_:field:recordIndex:) :

func numberForPlot(plot: CPTPlot, field fieldEnum: UInt, recordIndex idx: UInt) -> AnyObject? {
  let symbol = symbols[Int(idx)]
  let currencyRate = rate.rates[symbol.name]!.floatValue
  return 1.0 / currencyRate
}

餅圖會使用這個方法得到索引為 recordIndex 的貨幣符號的“總”值。

你應該注意到這個值并 不是 一個百分比值。取而代之的是,這個方法計算出了相對基準貨幣的貨幣匯率:返回的這個 1.0 / currencyRate 的值是"一個單位的基準貨幣是多少價值的另外的對照貨幣"的匯率。

CPTPieChart 將查看計算每個分塊的百分比值,這個值最終決定了這個分塊占多大。

下面,用下面這行替代掉 dataLabelForPlot(_:recordIndex:) :

func dataLabelForPlot(plot: CPTPlot, recordIndex idx: UInt) -> CPTLayer? {
  let value = rate.rates[symbols[Int(idx)].name]!.floatValue
  let layer = CPTTextLayer(text: String(format: "\(symbols[Int(idx)].name)\n%.2f", value))
  layer.textStyle = plot.labelTextStyle
  return layer
}

這個方法返回了餅圖分片的標簽。期望的返回類型 CPTLayer 和 CALayer 有點相似,但是 CPTLayer 更加抽象,在 Mac OS X 和 iOS 上都能用,還提供了額外的繪圖細節供 Core Plot 使用。

這里,創建并返回一個 CPTLayer 的子類 CPTTextLayer 去展示文本。

最后,將下面這段代碼替換掉 sliceFillForPieChart(_:, recordIndex:) 去添加分片的顏色:

func sliceFillForPieChart(pieChart: CPTPieChart, recordIndex idx: UInt) -> CPTFill? {
  switch idx {
  case 0:   return CPTFill(color: CPTColor(componentRed:0.92, green:0.28, blue:0.25, alpha:1.00))
  case 1:   return CPTFill(color: CPTColor(componentRed:0.06, green:0.80, blue:0.48, alpha:1.00))
  case 2:   return CPTFill(color: CPTColor(componentRed:0.22, green:0.33, blue:0.49, alpha:1.00))
  default:  return nil
  }
}

構建并運行,你就將看到一個漂亮的餅圖了:

等一下...圖例呢!

這個圖表看上去相當不錯,但是添加一個圖例應該會讓它更棒。接下來你將學習怎么添加一個圖例到這個圖表里。

首先,用下面這段替換掉 configureLegend() :

func configureLegend() {
  // 1 - Get graph instance
  guard let graph = hostView.hostedGraph else { return }

  // 2 - Create legend
  let theLegend = CPTLegend(graph: graph)

  // 3 - Configure legend
  theLegend.numberOfColumns = 1
  theLegend.fill = CPTFill(color: CPTColor.whiteColor())
  let textStyle = CPTMutableTextStyle()
  textStyle.fontSize = 18
  theLegend.textStyle = textStyle

  // 4 - Add legend to graph
  graph.legend = theLegend
  if view.bounds.width > view.bounds.height {
    graph.legendAnchor = .Right
    graph.legendDisplacement = CGPoint(x: -20, y: 0.0)

  } else {
    graph.legendAnchor = .BottomRight
    graph.legendDisplacement = CGPoint(x: -8.0, y: 8.0)
  }
}

同樣你也需要為每個分片提供圖例的數據。

要提供數據,就用下面這段替換掉 legendTitleForPieChart(_:recordIndex:) :

func legendTitleForPieChart(pieChart: CPTPieChart, recordIndex idx: UInt) -> String? {
  return symbols[Int(idx)].name
}

構建并運行,你就會得到一個“帶圖例的”圖表啦。

創建柱狀圖

看樣子你已經是繪制餅圖的專家啦,但是時候去搞一個柱狀圖了!

打開 BarGraphViewController 并添加下面這行:

import CorePlot

接著,再添加下面這行:

@IBOutlet var hostView: CPTGraphHostingView!

其實就和餅圖一樣,托管視圖將承載這個柱狀圖的展示。

下一步,添加下面這些屬性:

var plot1: CPTBarPlot!
var plot2: CPTBarPlot!
var plot3: CPTBarPlot!

這里聲明了三個 CPTBarPlot 類型的屬性,它們就相當于展示在圖表中的每種貨幣。

注意到同樣也有三個 IBOutlet 標簽和三個 IBAction 方法已經被定義了,你都可以在 storyboard 上看到它們。

最后,把下面這個類擴展添加到文件末尾:

extension BarGraphViewController: CPTBarPlotDataSource, CPTBarPlotDelegate {

  func numberOfRecordsForPlot(plot: CPTPlot) -> UInt {
    return 0
  }

  func numberForPlot(plot: CPTPlot, field fieldEnum: UInt, recordIndex idx: UInt) -> AnyObject? {
    return 0
  }

  func barPlot(plot: CPTBarPlot, barWasSelectedAtRecordIndex idx: UInt, withEvent event: UIEvent) {

  }
}

這和創建餅圖的過程太像了:通過 CPTBarPlotDataSource 為柱狀圖提供數據,通過 CPTBarPlotDelegate 捕捉用戶交互事件。你只需要復制粘貼就好了。

再次配置圖表托管視圖

就像剛剛創建餅圖時候一樣,再次需要通過界面生成器把托管視圖添加進去。

回到 Main.storyboard 并選擇 BarGraphViewController 窗口。

在視圖上拖拽出一個新的 UIView ,將它的類更改為 CPTGraphHostingView 并將其輸出連接到控制器里的 hostView 。

通過 UtilitiesSize Inspector (那個 刻度尺 選項卡)將它的框架更新到下面那樣:

X = 0, Y = 53, Width = 600, Height = 547

添加它和所有相鄰元素的約束,確認沒有設置 外邊距約束

最后,設置一個你喜歡的背景顏色。我再次用了92%透明度的灰度顏色。

繪制柱狀圖

既然 UI 已經通過上面的學習全部弄好了,是時候去繪制一個柱狀圖了。

首先,回到 BarGraphViewController ,你需要一對常量屬性。把下面這段添加到其他屬性之前:

let BarWidth = 0.25
let BarInitialX = 0.25

你還需要一個幫助函數去計算最高的率值。把下面這段添加到 updateLabels() 之后:

func highestRateValue() -> Double {
  var maxRate = DBL_MIN
  for rate in rates {
    maxRate = max(maxRate, rate.maxRate().doubleValue)
  }
  return maxRate
}

接著,把下面的方法添加到 highestRateValue() 之后:

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()
  initPlot()
}

func initPlot() {
  configureHostView()
  configureGraph()
  configureChart()
  configureAxes()
}

func configureHostView() {
}

func configureGraph() {
}

func configureChart() {
}

func configureAxes() {
}

是不是看上去很眼熟?是的,這些和之前的結構完全一樣。

下面這行添加到 configureHostView() 里:

hostView.allowPinchScaling = false

因為你不需要捏合縮放,所以你應該再次把它禁用。

接著,把下面那么多行代碼添加到 configureGraph() 里:

// 1 - Create the graph
let graph = CPTXYGraph(frame: hostView.bounds)
graph.plotAreaFrame?.masksToBorder = false
hostView.hostedGraph = graph

// 2 - Configure the graph
graph.applyTheme(CPTTheme(named: kCPTPlainWhiteTheme))
graph.fill = CPTFill(color: CPTColor.clearColor())
graph.paddingBottom = 30.0
graph.paddingLeft = 30.0
graph.paddingTop = 0.0
graph.paddingRight = 0.0

// 3 - Set up styles
let titleStyle = CPTMutableTextStyle()
titleStyle.color = CPTColor.blackColor()
titleStyle.fontName = "HelveticaNeue-Bold"
titleStyle.fontSize = 16.0
titleStyle.textAlignment = .Center
graph.titleTextStyle = titleStyle

let title = "\(base.name) exchange rates\n\(rates.first!.date) - \(rates.last!.date)"
graph.title = title
graph.titlePlotAreaFrameAnchor = .Top
graph.titleDisplacement = CGPointMake(0.0, -16.0)

// 4 - Set up plot space
let xMin = 0.0
let xMax = Double(rates.count)
let yMin = 0.0
let yMax = 1.4 * highestRateValue()
guard let plotSpace = graph.defaultPlotSpace as? CPTXYPlotSpace else { return }
plotSpace.xRange = CPTPlotRange(locationDecimal: CPTDecimalFromDouble(xMin), lengthDecimal: CPTDecimalFromDouble(xMax - xMin))
plotSpace.yRange = CPTPlotRange(locationDecimal: CPTDecimalFromDouble(yMin), lengthDecimal: CPTDecimalFromDouble(yMax - yMin))

下面是這段代碼邏輯的拆解:

  1. 首先,實例化一個 CPTXYGraph ,實際上就是一個柱狀圖,并將它關聯到 hostView 。

  2. 然后聲明一個 簡約的白色 默認主題并為了展示 XY 軸去設置左側和下方的內邊距。

  3. 接著設置文本樣式,圖表標題以及標題位置。

  4. 最后,配置 CPTXYPlotSpace ,它負責將設備的坐標系映射到圖表的坐標系。針對這個圖表,你正在繪制三個使用了相同坐標系的匯率。然而,也有可能每個條形圖的坐標系都是 分離 的。你還要在坐標系中假定一個最大最小值匯率范圍。在后面的教程中,你將學習到怎么樣在不提前設定范圍的情況下自動調節空間大小。

既然已經創建好圖表了,那是時候增加一些繪制方法進去了!把下面的代碼添加到 configureChart() 里:

// 1 - Set up the three plots
plot1 = CPTBarPlot()
plot1.fill = CPTFill(color: CPTColor(componentRed:0.92, green:0.28, blue:0.25, alpha:1.00))
plot2 = CPTBarPlot()
plot2.fill = CPTFill(color: CPTColor(componentRed:0.06, green:0.80, blue:0.48, alpha:1.00))
plot3 = CPTBarPlot()
plot3.fill = CPTFill(color: CPTColor(componentRed:0.22, green:0.33, blue:0.49, alpha:1.00))

// 2 - Set up line style
let barLineStyle = CPTMutableLineStyle()
barLineStyle.lineColor = CPTColor.lightGrayColor()
barLineStyle.lineWidth = 0.5

// 3 - Add plots to graph
guard let graph = hostView.hostedGraph else { return }
var barX = BarInitialX
let plots = [plot1, plot2, plot3]
for plot: CPTBarPlot in plots {
  plot.dataSource = self
  plot.delegate = self
  plot.barWidth = BarWidth
  plot.barOffset = barX
  plot.lineStyle = barLineStyle
  graph.addPlot(plot, toPlotSpace: graph.defaultPlotSpace)
  barX += BarWidth
}

接著來看看上面的代碼干了什么:

  1. 實例化每個條形圖并設置它們的填充色。

  2. 實例化一個代表每個條形圖的外部邊框的 CPTMutableLineStyle 實例。

  3. 給每個條形圖提供“共同配置”。該配置包括設置數據源和代理,寬度和每個條形圖在坐標系中的相對位置(左右)以及線條樣式,最后,添加這個坐標系到圖表當中。

雖然還不可以看到柱狀圖展示出來,但通過構建 app 可以去驗證目前為止是否所有代碼都可以正確編譯通過。

為了確切看到柱狀圖展示數據出來,需要去實現提供圖表所需數據的代理方法。

用下面這行替換掉 numberOfRecordsForPlot(:_) :

return UInt(rates.count ?? 0)

該方法返回了應該展示的記錄的總數。

下面這段替換掉 numberForPlot(_:field:recordIndex:) :

if fieldEnum == UInt(CPTBarPlotField.BarTip.rawValue) {
  if plot == plot1 {
    return 1.0
  }
  if plot == plot2 {
    return rates[Int(idx)].rates[symbols[0].name]!.floatValue
  }
  if plot == plot3 {
    return rates[Int(idx)].rates[symbols[1].name]!.floatValue
  }
}
return idx

CPTBarPlotField.BarTip 的值表明了柱狀圖的相對大小。在你需要取回數據的時候可以使用保留屬性計算出匯率, recordIndex 對應了利息率的位置。

構建并運行,你應該可以看到和下面這張圖一樣的情況:

已經快完成了!但請注意還沒有任何東西指明每個坐標軸是代表什么意思。

要解決這個問題,把下面這段添加進 configureAxes() :

// 1 - Configure styles
let axisLineStyle = CPTMutableLineStyle()
axisLineStyle.lineWidth = 2.0
axisLineStyle.lineColor = CPTColor.blackColor()

// 2 - Get the graph's axis set
guard let axisSet = hostView.hostedGraph?.axisSet as? CPTXYAxisSet else { return }

// 3 - Configure the x-axis
if let xAxis = axisSet.xAxis {
  xAxis.labelingPolicy = .None
  xAxis.majorIntervalLength = 1
  xAxis.axisLineStyle = axisLineStyle
  var majorTickLocations = Set<nsnumber>()
  var axisLabels = Set<cptaxislabel>()
  for (idx, rate) in rates.enumerate() {
    majorTickLocations.insert(idx)
    let label = CPTAxisLabel(text: "\(rate.date)", textStyle: CPTTextStyle())
    label.tickLocation = idx
    label.offset = 5.0
    label.alignment = .Left
    axisLabels.insert(label)
  }
  xAxis.majorTickLocations = majorTickLocations
  xAxis.axisLabels = axisLabels
}

// 4 - Configure the y-axis
if let yAxis = axisSet.yAxis {
  yAxis.labelingPolicy = .FixedInterval
  yAxis.labelOffset = -10.0
  yAxis.minorTicksPerInterval = 3
  yAxis.majorTickLength = 30
  let majorTickLineStyle = CPTMutableLineStyle()
  majorTickLineStyle.lineColor = CPTColor.blackColor().colorWithAlphaComponent(0.1)
  yAxis.majorTickLineStyle = majorTickLineStyle
  yAxis.minorTickLength = 20
  let minorTickLineStyle = CPTMutableLineStyle()
  minorTickLineStyle.lineColor = CPTColor.blackColor().colorWithAlphaComponent(0.05)
  yAxis.minorTickLineStyle = minorTickLineStyle
  yAxis.axisLineStyle = axisLineStyle
}</cptaxislabel></nsnumber>

簡單地說,上面的代碼首先為軸線和標題定義了樣式,然后,為圖表添加坐標軸的設置并配置好 x 軸和 y 軸的一些屬性。

構建并運行就可以看到這些改動的結果了。

功能化坐標軸

更棒了對吧?唯一的缺陷在于這個坐標軸太簡單了,沒辦法從這兒得到一個準確的匯率展示。

你可以修復這個問題以便當用戶點按在一個單獨的柱狀圖時,這個 app 可以展示這個圖表示的匯率。為了實現它,需要增加一個新的屬性:

var priceAnnotation: CPTPlotSpaceAnnotation?

然后把下面的代碼添加到 barPlot(_:barWasSelectedAtRecordIndex:) :

// 1 - Is the plot hidden?
if plot.hidden == true {
  return
}
// 2 - Create style, if necessary
let style = CPTMutableTextStyle()
style.fontSize = 12.0
style.fontName = "HelveticaNeue-Bold"

// 3 - Create annotation
guard let price = numberForPlot(plot,
                                field: UInt(CPTBarPlotField.BarTip.rawValue),
                                recordIndex: idx) as? CGFloat else { return }

priceAnnotation?.annotationHostLayer?.removeAnnotation(priceAnnotation)
priceAnnotation = CPTPlotSpaceAnnotation(plotSpace: plot.plotSpace!, anchorPlotPoint: [0,0])

// 4 - Create number formatter
let formatter = NSNumberFormatter()
formatter.maximumFractionDigits = 2
// 5 - Create text layer for annotation
let priceValue = formatter.stringFromNumber(price)!
let textLayer = CPTTextLayer(text: priceValue, style: style)

priceAnnotation!.contentLayer = textLayer
// 6 - Get plot index
var plotIndex: Int = 0
if plot == plot1 {
  plotIndex = 0
}
else if plot == plot2 {
  plotIndex = 1
}
else if plot == plot3 {
  plotIndex = 2
}
// 7 - Get the anchor point for annotation
let x = CGFloat(idx) + CGFloat(BarInitialX) + (CGFloat(plotIndex) * CGFloat(BarWidth))
let y = CGFloat(price) + 0.05
priceAnnotation!.anchorPlotPoint = [x, y]
// 8 - Add the annotation
guard let plotArea = plot.graph?.plotAreaFrame?.plotArea else { return }
plotArea.addAnnotation(priceAnnotation)

這里需要一些解釋:

  1. 不要給一個隱藏的柱狀圖展示注解,而當圖沒有設置隱藏屬性的時候,在把切換開關整合到圖表之后,你就將實現它了。

  2. 這里還要為你的注解創建一個文本樣式。

  3. 得到指定柱狀圖的匯率,然后如果它不存在一個注解對象,就創建一個。

  4. 如果沒有數值格式化的方法還需要創建一個,因為在匯率展示的時候需要先格式化它。

  5. 創建一個使用這個格式化匯率的文本層,并將注解的內容層設置到這個新的文本層上。

  6. 獲取你將展示的注解需要放置的柱狀圖索引。

  7. 基于這個索引計算注解的位置,并給使用這個計算位置注解設置 anchorPlotPoint 的值。

  8. 最后,將注解添加到圖表上。

構建并運行。每次當你點按圖表中的一個柱體時,該柱體所表示的值就應該正好在其上方彈出來。

棒極了! :]

隱藏和查找

這個柱狀圖看起來很棒,但屏幕最上方的切換開關并沒有起什么作用,是時候改動它們了。

首先,需要添加一個幫助方法,把下面這段添加到 switch3Changed(_:) 之后:

func hideAnnotation(graph: CPTGraph) {
  guard let plotArea = graph.plotAreaFrame?.plotArea,
    priceAnnotation = priceAnnotation else {
      return
  }

  plotArea.removeAnnotation(priceAnnotation)
  self.priceAnnotation = nil
}

這段代碼首先簡單地移除了一個如果存在的注解。

下一步,你希望用戶通過切換開關展示一個給定的貨幣匯率柱狀圖。

要做到這個功能,用下面這段替換到 switch1Changed(_:) , switch2Changed(_:) 和 switch3Changed(_:) 的實現。

@IBAction func switch1Changed(sender: UISwitch) {
  let on = sender.on
  if !on {
    hideAnnotation(plot1.graph!)
  }
  plot1.hidden = !on
}

@IBAction func switch2Changed(sender: UISwitch) {
  let on = sender.on
  if !on {
    hideAnnotation(plot2.graph!)
  }
  plot2.hidden = !on
}

@IBAction func switch3Changed(sender: UISwitch) {
  let on = sender.on
  if !on {
    hideAnnotation(plot3.graph!)
  }
  plot3.hidden = !on
}

這個邏輯相當簡單。如果開關設置了關閉,相關的圖和其可見的注解就將被隱藏,而如果設置為開啟,則圖就會被設置為可見。

構建并運行。現在你可以在圖表中隨意切換每個柱狀圖的展示了。教程至此已經完成了很不錯的工作!

接下來干點啥?

你可以從 這里 下載一個已完成的項目。

哇哦,相當有趣!這個教程重點介紹了 Core Plot 的強大功能并希望提示了你該怎么在你自己的 apps 里使用它。

當然還可以參考 Core Plot 倉庫獲取更多的信息,包括文檔,例子和一些小貼士。

還有,如果你對這個教程有任何的問題或者評論,歡迎加入下面的論壇進行討論。

祝你有個快樂的繪圖過程!

 

閱讀原文

 

 本文由用戶 leilei03 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!