LoginSignup
10
8

More than 3 years have passed since last update.

【swift】chartsを使って体重管理アプリを作ってみた

Last updated at Posted at 2019-09-29

入力値がグラフに反映される実装をしてみました

流れとしてはこんな感じ

初期画面で体重と体脂肪率を入力⇨登録ボタンで画面遷移、その日の情報が登録されてグラフに表示、グラフタップでその日の情報が表示されるって感じです。

コードメインになります。

メイン画面の方はこんな感じです。

main
import UIKit

class HomeViewController: UIViewController,UITextFieldDelegate,UITableViewDelegate,UITableViewDataSource {

    @IBOutlet weak var weightLabel: UITextField!
    @IBOutlet weak var fatLabel: UITextField!
    @IBOutlet weak var datePicker: UIButton!
    @IBOutlet weak var trainingView: UITableView!

    //UserDefaultsのインスタンス
    let userDefaults = UserDefaults.standard
    //日付表示変更用
    var changeDate : Date = Date()
    //テーブル表示用
    var trainList = [String]()
    //日付の取得
    let today: Date = Date()
    let dateFormatter = DateFormatter()

    override func viewDidLoad() {
        super.viewDidLoad()
        //datePickerからの日付を判定し、帰ってきた値をセットする
        dateFormatter.dateFormat = "yyyy/MM/dd"
        if changeDate == today {
            datePicker.setTitle(dateFormatter.string(from: today), for: .normal)
        } else {
            datePicker.setTitle(dateFormatter.string(from: changeDate), for: .normal)
        }
        //トレーニングリストの保存&呼び出し
        if userDefaults.array(forKey: "training") != nil {
            trainList = userDefaults.array(forKey: "training") as! [String]
            userDefaults.set(trainList, forKey: "training")
            userDefaults.synchronize()
        }
        //Delegate&DataSourceの呼び出し
        trainingView.delegate = self
        trainingView.dataSource = self
        trainingView.allowsMultipleSelection = true
        weightLabel.delegate = self
        fatLabel.delegate = self
    }
    //キーボード以外の箇所のタッチでキーボードを閉じる処理
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.view.endEditing(true)
    }
    //キーボードを閉じる処理
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
    //tableDelegate
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let cell = tableView.cellForRow(at: indexPath)
        cell?.accessoryType = .checkmark
    }
    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
        let cell = tableView.cellForRow(at:indexPath)
        cell?.accessoryType = .none
    }
    //TableDataSource
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return trainList.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel!.text = trainList[indexPath.row]
        cell.selectionStyle = .none
        let selectedIndexPaths = tableView.indexPathsForSelectedRows
        if selectedIndexPaths != nil && (selectedIndexPaths?.contains(indexPath))! {
            cell.accessoryType = .checkmark
        } else {
            cell.accessoryType = .none
        }
        return cell
    }

    @IBAction func saveMemory(_ sender: UIButton) {
        performSegue(withIdentifier: "saveMemory", sender: nil)
    }
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        //入力値に空があればエラーを出す
        if segue.identifier == "saveMemory" {
            let nextView = segue.destination as! ViewController
            //            空の場合は何もせず結果の画面へ遷移
            if weightLabel.text! != "" || fatLabel.text! != "" {
                let xs = self.trainingView.indexPathsForSelectedRows
                if xs != nil {
                    for x in xs! {
                        nextView.trainList.append(trainList[x.row])
                    }
                }
                nextView.weightText = Double(weightLabel.text!)!
                nextView.fatText = Double(fatLabel.text!)!
                nextView.dateText = datePicker.titleLabel!.text!
            }
        }
    }
}

下記がチャート画面です。

charts
import UIKit
import Charts

class ViewController: UIViewController,ChartViewDelegate,UITableViewDelegate,UITableViewDataSource {

    @IBOutlet weak var chartsView: LineChartView!
    @IBOutlet weak var dateLabel: UILabel!
    @IBOutlet weak var weightLabel: UILabel!
    @IBOutlet weak var fatLabel: UILabel!
    @IBOutlet var trainedList: UITableView!
    let userDefaults = UserDefaults.standard
    let weightString = "Weight"
    let fatString = "Fat"

    var weightDic = [String:Double]()
    var fatDic = [String:Double]()
    var trainDic = [String:[String]]()
    var weightList = [Double]()
    var fatList = [Double]()
    var weightText = 0.0
    var fatText = 0.0
    var dateText = ""
    var chartDataCount = 0
    var daysList = [String]()
    var trainList = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()
        chartsView.delegate = self

        let weightKey = weightString + dateText
        let fatKey = fatString + dateText

        loadData()
        if weightKey != "Weight" || fatKey != "Fat" {
            saveData(weightKey : weightKey, fatKey: fatKey, daysText:dateText)
        } else if dateText == "" {
            let weightSort = weightDic.keys.sorted()
            let fatSort = fatDic.keys.sorted()

            for key in weightSort {
                weightList.append(weightDic[key]!)
                daysList.append(String(key.suffix(5)))
            }
            for key in fatSort {
                fatList.append(fatDic[key]!)
            }
        }
        setChart(days:daysList)
        trainList = [String]()
    }
    @IBAction func clearUserDefault(_ sender: Any) {
        print("Clear")
        let alert: UIAlertController = UIAlertController(title: "データ消去", message: "これまでのデータを消去しますか?", preferredStyle: UIAlertController.Style.actionSheet)
        let comit = UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler:{
                (action: UIAlertAction!) -> Void in
            self.userDefaults.removeObject(forKey: "weight")
            self.userDefaults.removeObject(forKey: "fat")
            self.daysList = [""]
            self.weightList = [Double]()
            self.fatList = [Double]()
            self.dateLabel.text! = ""
            self.weightLabel.text! = ""
            self.fatLabel.text! = ""
            self.trainList = [String]()
            self.setChart(days: self.daysList)
            self.trainedList.reloadData()
            super.viewDidLoad()
        })
        let cancel = UIAlertAction(title: "戻る", style: UIAlertAction.Style.cancel, handler: nil)
        alert.addAction(comit)
        alert.addAction(cancel)
        present(alert, animated: true, completion: nil)
    }
    //userDefaultsからデータをDictionaryに代入
    //データがなければスルー(1回目とクリア後)
    func loadData() {
        if userDefaults.dictionary(forKey: "weight") != nil {
            weightDic = userDefaults.dictionary(forKey: "weight") as! [String : Double]
        }
        if userDefaults.dictionary(forKey: "fat") != nil {
            fatDic = userDefaults.dictionary(forKey: "fat") as! [String : Double]
        }
        if userDefaults.dictionary(forKey: "train") != nil {
            trainDic = userDefaults.dictionary(forKey: "train") as! [String:[String]]
        }
    }
    //渡ってきた値をuserDefaultsへセットし、リストへDictionaryValueset
    func saveData(weightKey : String,fatKey : String, daysText: String) {
        weightDic[weightKey] = weightText
        fatDic[fatKey] = fatText
        trainDic[daysText] = trainList

        userDefaults.set(weightDic, forKey: "weight")
        userDefaults.set(fatDic, forKey: "fat")
        userDefaults.set(trainDic, forKey: "train")
        userDefaults.synchronize()

        let weightSort = weightDic.keys.sorted()
        let fatSort = fatDic.keys.sorted()

        for key in weightSort {
            weightList.append(weightDic[key]!)
            daysList.append(String(key.suffix(5)))
        }
        for key in fatSort {
            fatList.append(fatDic[key]!)
        }
        setChart(days:daysList)
        trainList = [String]()
    }
    func setChart(days:[String]){
        let lineDefault = UIColor(red: 140.0/255.0, green: 234.0/255.0, blue: 255.0/255.0, alpha: 1.0)

        let data = LineChartData()
        var lineChartEntry1 = [ChartDataEntry]()

        for i in 0..<weightList.count {
            lineChartEntry1.append(ChartDataEntry(x: Double(i), y: Double(weightList[i])))
        }
        let line1 = LineChartDataSet(entries: lineChartEntry1, label: "体重")
        line1.drawCirclesEnabled = false
        line1.drawValuesEnabled = true
        line1.valueTextColor = UIColor.white
        line1.lineWidth = 2
        line1.setColor(UIColor.red)
        data.addDataSet(line1)

        if (fatList.count > 0) {
            var lineChartEntry2 = [ChartDataEntry]()
            for i in 0..<fatList.count {
                lineChartEntry2.append(ChartDataEntry(x: Double(i), y: Double(fatList[i])))
            }
            let line2 = LineChartDataSet(entries: lineChartEntry2, label: "体脂肪")
            line2.drawCirclesEnabled = false
            line2.drawValuesEnabled = true
            line2.valueTextColor = UIColor.white
            line2.lineWidth = 2
            line2.setColor(UIColor.blue)
            data.addDataSet(line2)
        }
        let chartFormatter = LineChartFormatter(labels: days)
        let xAxis = chartsView.xAxis
        xAxis.valueFormatter = chartFormatter
        xAxis.labelFont = UIFont(name: "HelveticaNeue-Light", size: 12.0)!
        xAxis.labelTextColor = UIColor.white
        chartsView.xAxis.granularityEnabled = true
        chartsView.xAxis.granularity = 1.0
        chartsView.xAxis.decimals = 0
        chartsView.xAxis.valueFormatter = xAxis.valueFormatter
        chartsView.data = data
    }

    private class LineChartFormatter: NSObject, IAxisValueFormatter {

        var labels: [String] = []

        func stringForValue(_ value: Double, axis: AxisBase?) -> String {
            if labels.count == 1 {
                return labels[0]
            } else {
                return labels[Int(value)]
            }
        }
        init(labels: [String]) {
            super.init()
            self.labels = labels
        }
    }
    func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
        let weightSort = weightDic.keys.sorted()
        let fatSort = fatDic.keys.sorted()
        var weightKey :String
        var fatKey : String
        var dateString : String

        if highlight.dataSetIndex == 0 {
            weightKey  = weightSort[NSInteger(entry.x)]
            fatKey = "Fat" + weightSort[NSInteger(entry.x)].suffix(10)
            dateString = String(weightSort[NSInteger(entry.x)].suffix(10))
        } else {
            weightKey  = "Weight" + fatSort[NSInteger(entry.x)].suffix(10)
            fatKey = fatSort[NSInteger(entry.x)]
            dateString = String(fatSort[NSInteger(entry.x)].suffix(10))
        }
        let weightString = weightDic[weightKey]
        let fatString = fatDic[fatKey]
        trainDic = userDefaults.dictionary(forKey: "train") as! [String : [String]]
        print(trainDic)
        if trainDic[dateString] != nil {
            trainList = trainDic[dateString]!
        } else {
            trainList = [String]()
        }

        dateLabel.text = dateString
        dateLabel.textColor = UIColor.white
        weightLabel.text = String(weightString!) + "kg"
        weightLabel.textColor = UIColor.white
        fatLabel.text = String(fatString!) + "%"
        fatLabel.textColor = UIColor.white
        trainedList.reloadData()
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return trainList.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel!.text = trainList[indexPath.row]
        return cell
    }
}

もっと綺麗なやり方があるんだろうなあ。。。

一応画面です。

IMG_3066.PNG
IMG_3067.PNG

恥ずかしながらまだSEなんですがw

参考になればいいし、もっとこうした方がってのがあれば教えてください!

profile

生まれも育ちも大阪の浪速中の浪速っ子が30才未経験からITエンジニアとして生きるブログもやってます。
よかったらみてください:muscle_tone2:

PVアクセスランキング にほんブログ村

10
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
8