マンガ読書管理アプリの設計メモ(21)
今回は、統計画面でグラフ表示するところなどを書きたいと思います。
1.StoryboardでUI部品をポチポチ配置します。
正直、わりとトリッキーな配置をしてます。
この画面はUITableViewControllerを使っており、StaticCellでデザインしています。動的にCellを生成しないので、そういう方法で実装してみました。(これがベストプラクティスかは謎)
1セクション目は、「年数を表示し、右と左のボタンで年数を変えることで、グラフが変わる処理を行う部分」と「グラフ部分」と「余白調整用」の3つのViewをセル内を含めています。
1つ目は、StackViewを使うことで均等に配置するレイアウトを実現してます。
2つ目は、グラフ部分でChartsライブラリを活用しており、そのクラスを使うためにViewを配置し「BarChartView」クラスを設定しています。
3つ目は、単純に無理やり余白を追加しただけの美しくないもの部分になってます。。。
また2セクション目は、4つの統計情報を表示するStaticセルを追加しています。アイコンはiOS13なら標準でプリセットされているものを使っています。
2.グラフ表示部分を作成する。
基本はライブラリの使い方に沿って実装していきます。もちろんCocoaPodsでダウンロードしておきます。もろもろ概要は以下をご参考に。
最初はライブラリをインポート。
import Charts
class DataTableViewController: UITableViewController {
@IBOutlet weak var barChartView: BarChartView!
var monthReadingBookCounts: [Double] = []
グラフ描画部分はこんな感じ。monthReadingBookCountsに各月のデータを入れておきます。
func barChartUpdate () {
// データを入れてます。
let month1 = BarChartDataEntry(x: 1.0, y: monthReadingBookCounts[0])
let month2 = BarChartDataEntry(x: 2.0, y: monthReadingBookCounts[1])
let month3 = BarChartDataEntry(x: 3.0, y: monthReadingBookCounts[2])
let month4 = BarChartDataEntry(x: 4.0, y: monthReadingBookCounts[3])
let month5 = BarChartDataEntry(x: 5.0, y: monthReadingBookCounts[4])
let month6 = BarChartDataEntry(x: 6.0, y: monthReadingBookCounts[5])
let month7 = BarChartDataEntry(x: 7.0, y: monthReadingBookCounts[6])
let month8 = BarChartDataEntry(x: 8.0, y: monthReadingBookCounts[7])
let month9 = BarChartDataEntry(x: 9.0, y: monthReadingBookCounts[8])
let month10 = BarChartDataEntry(x: 10.0, y: monthReadingBookCounts[9])
let month11 = BarChartDataEntry(x: 11.0, y: monthReadingBookCounts[10])
let month12 = BarChartDataEntry(x: 12.0, y: monthReadingBookCounts[11])
let dataSet = BarChartDataSet([month1, month2, month3,month4,month5,month6,month7,month8,month9,month10,month11,month12])
// 後述しますが、値の少数点を消しています
dataSet.valueFormatter = BarChartValueFormatter()
// グラフの色
dataSet.colors = [orangeThemeColor]
// 読んだ漫画の本数が0ならグラフを表示させない。
if (emptyCount == 12){
dataSet.drawValuesEnabled = false
}
else{
dataSet.drawValuesEnabled = true
}
let data = BarChartData(dataSets: [dataSet])
barChartView.data = data
//x軸設定
barChartView.xAxis.labelPosition = .bottom //x軸ラベル下側に表示
barChartView.xAxis.labelFont = UIFont.systemFont(ofSize: 11) //x軸のフォントの大きさ
barChartView.xAxis.labelCount = Int(12) //x軸に表示するラベルの数
barChartView.xAxis.labelTextColor = .black //x軸ラベルの色
barChartView.xAxis.drawGridLinesEnabled = false
//y軸設定
barChartView.rightAxis.enabled = false
barChartView.leftAxis.enabled = false
barChartView.leftAxis.drawAxisLineEnabled = false //y左軸の表示(今回は表示しない)
//その他
barChartView.legend.enabled = false //"■ months"のlegendの表示
barChartView.dragDecelerationEnabled = false //指を離してもスクロール続くか
barChartView.chartDescription?.text = nil //Description(今回はなし)
barChartView.animate(yAxisDuration: 1)
//ピンチでズームが可能か
barChartView.pinchZoomEnabled = false
//ダブルタップでズームが可能か
barChartView.doubleTapToZoomEnabled = false
//ドラッグ可能か
barChartView.dragEnabled = false
//タップ時のハイライトを消す処理
barChartView.highlightPerTapEnabled = false
barChartView.highlightPerDragEnabled = false
barChartView.notifyDataSetChanged()
}
また、このままだと値が少数点が表示されてしまうので、文字のフォーマットを以下のメソッドにて整数にしています。
public class BarChartValueFormatter: NSObject, IValueFormatter{
public func stringForValue(_ value: Double, entry: ChartDataEntry, dataSetIndex: Int, viewPortHandler: ViewPortHandler?) -> String{
return String(Int(entry.y))
}
}
グラフの出来上がりはこんなイメージになります。
3.静的セル部分を作成する。
静的セルなので、表示するセクション数やセル数はハードコーディングしています。
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0: // 「グラフ」のセクション
return 1
case 1: // 「データ」のセクション
return 4
default:
return 0
}
}
実際の値の配置部分ですが、静的セルなので、静的セル内に配置したラベルなどはUITableViewContorollerクラスと紐付けさせておき、このクラス内で値をセットしてあげれば問題なく表示してくれます。
class DataTableViewController: UITableViewController {
@IBOutlet weak var yearLabel: UILabel!
@IBOutlet weak var barChartView: BarChartView!
// アプリの継続日数
@IBOutlet weak var appUseDayLabel: UILabel!
// アプリの継続日数を設定
let appUseDay = getAppUseDay()
appUseDayLabel.text = String(appUseDay) + "日"
func getAppUseDay() -> Int{
let realm = try! Realm()
let userStatistics = realm.objects(UserStatistics.self)
let dateFormater = DateFormatter()
dateFormater.locale = Locale(identifier: "ja_JP")
dateFormater.dateFormat = "yyyy/MM/dd"
// 初回起動日
let firstOpendate = dateFormater.date(from: userStatistics.first!.firstOpenDay)!
// 現在日
let nowOpenString = getNowDayString()
let nowOpendate = dateFormater.date(from: nowOpenString)
// 経過日数の取得
let elapsedDays = Calendar.current.dateComponents([.day], from: firstOpendate, to: nowOpendate!).day! + 1
return elapsedDays
}
これは1つの例ですが、同じようにすれば以下のようなイメージで実装できます。
この画面のポイントとなる部分は以上です。次は、マンガ読書管理アプリの設計メモ(20)でも説明した人気の漫画や新着の漫画を表示する画面について書こうと考えてます。
また、今回の部分、もしくは今までのところでも良いので、この辺りはもっと詳しく、という話があれば、コメントいただければと思います!
では!
素敵なアプリやサービスが作れるようにひとりで開発を頑張っています。応援してくれると嬉しいです!