🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

1060閲覧

Swift UIDatePickerとUITableViewでデータを連携させたい

maplemee

総合スコア16

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2021/02/03 12:34

実現したいこと
swift初学者ですが、食べたものを日記のように記録できる簡易な記録アプリを作っています。
食べたものを後から日を遡って記録できるように、
食べた日付をUIDatePickerで選択し、
選択した日付のセクションにある文字(食べ物)を表示する配列に
ピンクのUITextfieldから記録をしたい食べたものを入力しButtonを押すと追加できるようにしたい。
イメージ説明

以下、現在のソースコードです。

Swift5

1import UIKit 2 3class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource { 4 5 @IBOutlet weak var table: UITableView! 6 @IBOutlet weak var foodtextField: UITextField! 7 @IBOutlet weak var textField: UITextField! 8 9 var datePicker: UIDatePicker = UIDatePicker() 10 11 // section毎に配列を用意 12 var Array1: Array = ["コンビニ弁当"] 13 var Array2: Array = ["イタリアン"] 14 var Array3: Array = ["カレー"] 15 var Array4: Array = [""] 16   〜〜省略〜〜 17 var Array26: Array = [""] 18 var Array27: Array = [""] 19 var Array28: Array = [""] 20 21 22 // Sectionのタイトル 23 let sectionTitle = ["2/1","2/2","2/3","2/4","2/5","2/6","2/7","2/8","2/9","2/10", 24              "2/11","2/12","2/13","2/14","2/15","2/16","2/17","2/18","2/19","2/20", 25              "2/21","2/22","2/23","2/24","2/25","2/26","2/27","2/28"] 26 27 override func viewDidLoad() { 28 super.viewDidLoad() 29 // Do any additional setup after loading the view. 30 table.delegate = self 31 table.dataSource = self 32 33 // ピッカー設定 34 datePicker.datePickerMode = UIDatePicker.Mode.date 35 datePicker.timeZone = NSTimeZone.local 36 datePicker.locale = Locale.current 37 textField.inputView = datePicker 38 39 // 決定バーの生成 40 let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 35)) 41 let spacelItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil) 42 let doneItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(buttonon(_:))) 43 toolbar.setItems([spacelItem, doneItem], animated: true) 44 45 // インプットビュー設定 46 textField.inputView = datePicker 47 textField.inputAccessoryView = toolbar 48 49 } 50 51 // Section数 52 func numberOfSections(in tableView: UITableView) -> Int { 53 return sectionTitle.count 54 } 55 // Sectioのタイトル 56 func tableView(_ tableView: UITableView,titleForHeaderInSection section: Int) -> String? { 57 return sectionTitle[section] 58 } 59 60 // Table Viewのセルの数を指定 61 func tableView(_ table: UITableView,numberOfRowsInSection section: Int) -> Int { 62 if section == 0 { 63 return Array1.count 64 } 65 else if section == 1 { 66 return Array2.count 67 } 68 else if section == 2 { 69 return Array3.count 70 } 71 else if section == 3 { 72 return Array4.count 73 } 74 else if section == 4 { 75 return Array5.count 76 } 77 else if section == 5 { 78 return Array6.count 79 } 80 〜〜省略〜〜 81 else{ 82 return 0 83 } 84 } 85 86 //各セルの要素を設定する 87 func tableView(_ table: UITableView,cellForRowAt indexPath: IndexPath) -> UITableViewCell { 88 89 // tableCell の ID で UITableViewCell のインスタンスを生成 90 let cell = table.dequeueReusableCell(withIdentifier: "tableCell",for: indexPath) 91 92 // Tag番号 1 で UILabel インスタンスの生成 93 let label = cell.viewWithTag(1) as! UILabel 94 95 // Section毎に処理を分ける、ちょっと冗長的です 96 if indexPath.section == 0 { 97 //cell.textLabel?.text? = imgArray1[indexPath.row] as! String 98 99 label.text = String(describing: Array1[indexPath.row]) 100 } 101 else if indexPath.section == 1 { 102 label.text = String(describing: Array2[indexPath.row]) 103 } 104 else if indexPath.section == 2 { 105 label.text = String(describing: Array3[indexPath.row]) 106 } 107 else if indexPath.section == 3 { 108 label.text = String(describing: Array4[indexPath.row]) 109 } 110 else if indexPath.section == 4 { 111 label.text = String(describing: Array5[indexPath.row]) 112 } 113 114       〜〜以下省略〜〜 115 116 return cell 117 } 118 119 func tableView(_ table: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 120 return 40.0 121 } 122 123 @IBAction func buttonon(_ sender: Any) { 124 125 textField.endEditing(true) 126 127 // 日付のフォーマット 128 let formatter = DateFormatter() 129 formatter.dateFormat = "MM/dd HH:mm" 130 textField.text = "(formatter.string(from: Date()))" 131 132 table.reloadData() 133 } 134 135}

何か良い方法はございますでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

本当ならば、Realm なり、クラウド(mBaaS)などのデータベースと連携させた方がより柔軟で見通しの良い作りとなるのですが、とりあえず現在の構成をなるべく変更しない方法で考えてみたいと思います。

まず、28日分の配列を個別に用意するのは現実的ではないので、28日分の食事は二次元配列で管理したいと思います。

Swift

1 // MARK: section毎に配列を用意 2 // 28日分の配列を用意する 3 var foods: [[String]] = []

この様に宣言することで、[String]を含む配列(Array)を作ることができます。
この段階ではとりあえず中身は空にしておきます(repeatingを使ってこの段階で中身を入れることも可能ですが、とりあえずそれは置いておきます)。

また、28日分の日付を作るのも現実的ではないので、とりあえず配列だけ用意しておきます。

Swift

1 // MARK: Sectionのタイトル 2 var sectionTitle: [String] = []

次に、これらの配列に具体的に中身を入れていきます。
日付については、

Swift

1 // MARK: 日付タイトルを作る 2 for i in 1...28 { 3 sectionTitle.append("2/(i)") 4 }

こんな感じで作った方が楽に作れます。

食べたものについては、とりあえず28日分の要素だけ作っておきます。

Swift

1 // MARK: 食べたものの初期データを作る 2 for _ in 1...28 { 3 // からの配列だけを追加する 4 foods.append([]) 5 }

保存したり読み込んだりすることは、先の段階で考えてみてください。

ところで、食べたものの記録はどの様に行えばよいのでしょうか。
それは、食べた日に相当する要素に対して、食べたものを追加する、とう考え方でやりたいと思います。

たとえば、2月1日は初期状態では空の状態です。
つまり、

food[0] -> []

と中身がからの状態になっています(上記は Swift の表記ではなく、あくまでもイメージです)
また、配列の要素は0から始めるのが慣習なので、日付より1少なくなる点にも注意してください。

ここで、2月1日に「ラーメン」を食べたとしましょう。
何らかの方法で追加すると

food[0] -> ["ラーメン"]

となります。
同じ日に「ニラレバ」を追加したとすれば

food[0] -> ["ラーメン", "ニラレバ"]

という感じでデータを追加します。

food[]は28日分のデータを持った配列なので、最初の要素(subscripton)にアクセスすれば、その日に食べた物を全て得ることが可能です。

ちなみに、2月1日に最初に食べたものは

food[0][0] -> "ラーメン"

みたいな感じで逐次得ることが可能です。

したがって、UITableView のセクション内の要素数は

Swift

1 // Table Viewのセルの数を指定 2 func tableView(_ table: UITableView,numberOfRowsInSection section: Int) -> Int { 3 // MARK: 対応する日付の要素数を返す 4 // section = 5 return foods[section].count 6 }

という感じで、該当する日の要素数(食べたものの数)を返すようにします。section == 日付 - 1です。

食べたものの詳細については、

Swift

1//各セルの要素を設定する 2 func tableView(_ table: UITableView,cellForRowAt indexPath: IndexPath) -> UITableViewCell { 3 4 // tableCell の ID で UITableViewCell のインスタンスを生成 5 let cell = table.dequeueReusableCell(withIdentifier: "tableCell",for: indexPath) 6 7 cell.textLabel?.text = foods[indexPath.section][indexPath.row] 8 9 return cell 10 }

この様にすることで、該当するセクション(つまり日付)に対し、その日食べたものを一行ずつ表示させることが可能です。

ただ、最初にも述べた通り、これはあくまでも現状をなるべく変更しない方法で記述させる方法であり、最適解ではありません。

もし、Realm などのデータベースを使った、より適切な解決方法を知りたいようであれば、「Realm ToDoアプリ」みたいな感じで調べていくと、いろいろ習作が出てくるかと思います。

ちなみに、ご提示いただいたコードは次の様に変更してあります。
多少コメントも入れていますので、それも参考によんでみてください。

Swift

1import UIKit 2 3class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource { 4 5 @IBOutlet weak var table: UITableView! 6 @IBOutlet weak var foodtextField: UITextField! 7 @IBOutlet weak var textField: UITextField! 8 9 var datePicker: UIDatePicker = UIDatePicker() 10 11 // MARK: section毎に配列を用意 12 // 28日分の配列を用意する 13 var foods: [[String]] = [] 14 15 // MARK: Sectionのタイトル 16 var sectionTitle: [String] = [] 17 18 override func viewDidLoad() { 19 super.viewDidLoad() 20 21 // MARK: 日付タイトルを作る 22 for i in 1...28 { 23 sectionTitle.append("2/(i)") 24 } 25 26 // MARK: 食べたものの初期データを作る 27 for _ in 1...28 { 28 // からの配列だけを追加する 29 foods.append([]) 30 } 31 32 // Do any additional setup after loading the view. 33 table.delegate = self 34 table.dataSource = self 35 36 // ピッカー設定 37 datePicker.datePickerMode = UIDatePicker.Mode.date 38 datePicker.timeZone = NSTimeZone.local 39 datePicker.locale = Locale.current 40 // MARK: 以前のホイール形式の datePicker にする 41 datePicker.preferredDatePickerStyle = .wheels 42 textField.inputView = datePicker 43 44 // MARK: 食べたものを入力する UITextFiled に何か変化が生じたら delegate が呼ばれる様にする 45 foodtextField.delegate = self 46 47 // 決定バーの生成 48 let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 35)) 49 let spacelItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil) 50 let doneItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(buttonon(_:))) 51 toolbar.setItems([spacelItem, doneItem], animated: true) 52 53 // インプットビュー設定 54 textField.inputView = datePicker 55 textField.inputAccessoryView = toolbar 56 57 } 58 59 // MARK: Accessory View のボタン 60 @objc func buttonon(_ sender: Any) { 61 textField.endEditing(true) 62 // 日付のフォーマット 63 let formatter = DateFormatter() 64 formatter.dateFormat = "MM/dd HH:mm" 65 // MARK: Date() だと今日の日付になるので、datePicker.date で選択した日付を入れる 66 textField.text = "(formatter.string(from: datePicker.date))" 67 } 68 69 // Section数 70 func numberOfSections(in tableView: UITableView) -> Int { 71 return sectionTitle.count 72 } 73 // Sectioのタイトル 74 func tableView(_ tableView: UITableView,titleForHeaderInSection section: Int) -> String? { 75 return sectionTitle[section] 76 } 77 78 // Table Viewのセルの数を指定 79 func tableView(_ table: UITableView,numberOfRowsInSection section: Int) -> Int { 80 // MARK: 対応する日付の要素数を返す 81 // section = 82 return foods[section].count 83 } 84 85 //各セルの要素を設定する 86 func tableView(_ table: UITableView,cellForRowAt indexPath: IndexPath) -> UITableViewCell { 87 88 // tableCell の ID で UITableViewCell のインスタンスを生成 89 let cell = table.dequeueReusableCell(withIdentifier: "tableCell",for: indexPath) 90 91 cell.textLabel?.text = foods[indexPath.section][indexPath.row] 92 93 return cell 94 } 95 96 func tableView(_ table: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 97 return 40.0 98 } 99 100 @IBAction func setFoodButtonon(_ sender: Any) { 101 // MARK: datePicker で選んだ日付から、「日」だけを選ぶ 102 let selectedDate = Calendar.current.component(.day, from: datePicker.date) 103 // MARK: (日付 - 1) 日の要素に食べた食事を追加する 104 foods[selectedDate - 1].append(foodtextField.text!) 105 106 table.reloadData() 107 } 108 109} 110 111// Enter キーを押すとキーボードを隠す 112extension ViewController: UITextFieldDelegate { 113 func textFieldShouldReturn(_ textField: UITextField) -> Bool { 114 textField.endEditing(true) 115 } 116}

投稿2021/02/03 16:23

TsukubaDepot

総合スコア5086

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maplemee

2021/02/04 06:33

詳しい解説と全体コードによる丁寧な解説ありがとうございますm(_ _)m 実現したいことが全て実現できました。ベストアンサーとさせていただきました。 ご指摘の通り、記録をするアプリなのでデータベースとの連携を模索していましたが、 TableViewの仕組みにいまいち慣れていない段階でしたので後回しにしております。 二次元配列という言葉が出てきたのでTableViewで多次元配列を扱った初歩的な記事があまり検索しても出てこず、少し理解するのに時間を取りましたがその後おおよその動きは掴めました。 自分なりにsectionとrowが二次元配列になっている部分が難しいポイントだったので 以下のように置き換えて理解に繋げております。 foods[indexPath.section][indexPath.row] ↓ foods[0] ["ラーメン", "ニラレバ"] ・ ・ ・ foods[27][indexPath.row] 配列の用意に関しても今回のコードを参考にして効率的な書き方で進めていこうと思いますm(_ _)m
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問