LoginSignup
12
12

More than 5 years have passed since last update.

【Swift】Dictionary(grouping by )を使ってセクション分けしたTableViewを作る

Last updated at Posted at 2018-02-05

使ったことがなかったのですが
昨日たまたま活用する機会があったので
忘れないように記録します。

以下はスケジュールを日付別に時間順で並べています。

TableView.swift

import UIKit
import PlaygroundSupport

struct Plan {
    let name: String
    let detail: String
    let date: Date
}

class MyViewController : UIViewController {

    var schedules = [Date: [Plan]]()
    var dateOrder = [Date]()

    private var formatter: DateFormatter  {
        let f = DateFormatter()
        f.dateStyle = .long
        f.timeStyle = .none
        f.locale = Locale(identifier: "ja_JP")
        return f
    }

    private var timeFormatter: DateFormatter  {
        let f = DateFormatter()
        f.dateStyle = .none
        f.timeStyle = .short
        f.locale = Locale(identifier: "ja_JP")
        return f
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        prepare()

        let tv = UITableView(frame: self.view.frame)
        tv.dataSource = self
        tv.delegate = self
        view.addSubview(tv)
    }

    private func prepare() {
        let f = DateFormatter()
        f.locale = Locale(identifier: "en_US_POSIX")
        f.dateFormat = "yyyy/MM/dd HH:mm"

        let plans = [
            Plan(name: "休み", detail: "実家で過ごす", date: f.date(from: "2018/01/01 09:00")!),
            Plan(name: "外出", detail: "初詣", date: f.date(from: "2018/01/03 10:00")!),
            Plan(name: "仕事", detail: "社内会議", date: f.date(from: "2018/01/03 17:30")!),
            Plan(name: "外出", detail: "買い出し", date: f.date(from: "2018/01/15 19:00")!),
            Plan(name: "仕事", detail: "Team Meeting", date: f.date(from: "2018/01/15 15:30")!),
            Plan(name: "休み", detail: "家族で水族館", date: f.date(from: "2018/01/17 08:00")!),
            Plan(name: "休み", detail: "飲み会", date: f.date(from: "2018/01/17 20:00")!)
        ]

        schedules = Dictionary(grouping: plans) { plan -> Date in
            f.dateFormat = "yyyy/MM/dd"
            let s = f.string(from: plan.date)
            return f.date(from: s)!
        }
        .reduce(into: [Date: [Plan]]()) { dic, tuple in
            dic[tuple.key] = tuple.value.sorted { $0.date < $1.date }
        }

        // 日付順を保持するための配列
        dateOrder = Array(schedules.keys).sorted { $0 < $1 }
     }

}

extension MyViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let targetDate = dateOrder[section]
        return schedules[targetDate, default: []].count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let reuseIdentifier = "cell"

        var cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier)
        if cell == nil {
            cell = UITableViewCell(style: .subtitle, reuseIdentifier: reuseIdentifier)
        }

        let targetDate = dateOrder[indexPath.section]
        guard let plan = schedules[targetDate]?[indexPath.row] else {
            return cell!
        }

        cell!.textLabel?.text = plan.name

        let time = timeFormatter.string(from: plan.date)
        cell!.detailTextLabel?.text = "\(plan.detail) \(time)予定"
        return cell!
    }
}

extension MyViewController: UITableViewDelegate {

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

        let targetDate = dateOrder[section]

        let label = UILabel()
        label.backgroundColor = .lightGray
        label.textColor = .white

        label.text = formatter.string(from: targetDate)

        return label
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 60.0
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return schedules.keys.count
    }
}

let viewController = MyViewController()
let nv = UINavigationController(rootViewController: viewController)
nv.view.frame = CGRect(x: 0, y: 0, width: 320, height: 580)

// Present the view controller in the Live View window
PlaygroundPage.current.liveView = nv.view

以下のようになりました。

20180206.png

疑問

そういえば、Dictionaryにデフォルト値を設定できるようになったことも思い出し
使ってみましたが、どういう時に使えばいいのかがよくわかっていません。。。

存在しない場合は0.swift
// schedules[targetDate]?.count ?? 0 の方がわかりやすい?
schedules[targetDate, default:[]].count
クラッシュする.swift

// 存在しないtargetDateを設定した場合

// guardの中に入る
guard let plan = schedules[targetDate]?[indexPath.row] else {
    return cell!
}

// Optionalではないのでguardチェックできず
// Fatal error: Index out of rangeになる
// こういう使い方はNG?
let plan = schedules[targetDate, default:[]][indexPath.row]

何か良い使い方などございましたらぜひ教えてください:bow_tone1:

12
12
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
12
12