質問: tableData[section] return self.tableData.count } } ``` 前回の質問 テーブルビューを作成中の「Cannot use instance member...」エラー
回答: 前回の質問にあるようなSwift言語特有の事情に基づくエラーと異なり、今回のようにデータ型に関する勘違いが原因の場合には、エラーメッセージ等で検索をかけても解決策を見つけるのは難しいでしょう。 あなたのコードで`tableData`のデータ型は`[[String: Bool]]`(これは`Array>`と同じ)であり、『「`String`から`Bool`への辞書型」を要素とする配列型』であると言うことです。 一旦要素型のことを置いておけば、 * **`tableData`は配列(`Array`)型である** と言うことですね。 あなたは`tableData`に5つの辞書型データ(`checkListItem1`〜`checkListItem5`)を入れていますが、Swift言語やiOSのフレームワークがその中からうまくいいものを選んで`tableData`を辞書型としてアクセスできるようにする、なんてことは起こりません。 ***1Ambiguous reference to member 'subscript'*** ※行番号はStackOverflowのコードサンプルでは表示されない上に、数字の方も大きくずれてしまっているようですね。`//<-1`などのようにソースコード中にコメントの形でエラーが発生する行を明示してもらうと間違いもなくエラー箇所が見やすくなるかと思います。 というわけで1はこの箇所のことでしょうか? ``` let sectionData = tableData[(indexPath as NSIndexPath).section] let cellData = sectionData[(indexPath as NSIndexPath).row] //<-1 ``` 先に書いたように、`tableData`は『「`String`から`Bool`への辞書型」を要素とする配列型』ですから、それに整数値(`(indexPath as NSIndexPath).section`、ちなみにこれは`indexPath.section`と書けます)で添字付けして取り出した`sectionData`は「`String`から`Bool`への辞書型」と言うことになります。`[]`の中に入れて使えるのは`String`型の値(他にもあるのですが普通使わないので省略)だけで、整数型の値である`(indexPath as NSIndexPath).row`(これも`indexPath.row`で構いません)は使用できないと言うことです。 エラーメッセージにあるように*Ambiguous*(曖昧)と言われてしまうと、曖昧さをなくしてはっきりさせるような手段があるのかと思ってしまいますが、この辺のSwiftのエラーメッセージはまだまだ発展途上で、「'subscript'に関してデータ型に関するエラーがある」程度の意味として読んでやらないといけません。 ***2Value of type '[[String : Bool]]' has no member 'updateValue'*** ここと: ``` self.tableData.updateValue(false, forKey: cellText!) //<-2 ``` ここのことでしょうか: ``` self.tableData.updateValue(true, forKey: cellText!) //<-2 ``` まさに最初に書いたことがそのまま当てはまるのですが、`updateValue(_:forKey:)`は、辞書型に対して定義されているメソッドですので、配列型に対しては使えません。配列型である`[[String : Bool]]`には`updateValue`なんてメソッドもプロパティもない(メソッドやプロパティを総称して*member*と言っています)ですよと言うエラーです。 ※ちなみにSwiftの辞書型の値を単に更新したい場合、普通`updateValue(_:forKey:)`は使いません。 ***3Value of type '[[String : Bool]]' has no member 'keys'*** こちらのことでしょうか。 ``` var keys = String //<-3 ``` ほとんど同じことの繰り返しになりますが、`tableData`は配列型であるのに(辞書型のプロパティである)`keys`を使っていると言うことで、2と同じエラーが表示されているわけです。 どう修正するのか 最初に書いたように`tableData`は配列で5個の要素を持っているのに、その中の1個を選ばずに(まるで1個が自動的に選ばれて)辞書型になっているかのようにアクセスしているのが問題なのですから、「自分でちゃんと`tableData`の中から1個を選んでやって、その1個を辞書型として使う」ようにすれば良いと言うことになります。 ついでに関連する部分の修正も入れてしまいましたが、具体的には以下のようになります。`viewDidLoad()`以下の3メソッドが修正対象です。 ``` // セルの作成 // func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { //###まず最初に`tableData`の中からどの1個を使うのか、選ばないといけない let sectionData = tableData[indexPath.section] //<-`sectionData`は辞書型(`[String: Bool]`)になる //###`sectionData`(辞書型)のキーの配列を取得して並び替え //###`sectionData.keys`はもともと`[String]`型になるので型変換は不要 //###並べ替え前のデータは不要なので、`sorted()`を使って並べ替え後のデータだけを作成する let keys = sectionData.keys.sorted() //<-3 // キーの文字列を取得 let cellText = keys[indexPath.row] // セルの作成とテキストの設定 //###ここは本来なら //let cell = tableView.dequeueReusableCell(withIdentifier: "cell") //または //let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) //としないといけないのだが、質問のエラーに関係ない部分で長くなるのでとりあえずそのまま let cell = UITableViewCell(style: .default, reuseIdentifier: "cell") cell.textLabel?.text = cellText //###`sectionData`は`[String: Bool]`なので`String`型の値で添字付け //###`cellData`は`Bool?`(==`Optional`)型になる //###本来は`cellData`のように抽象的な名前でなく`cellIsChecked`などのわかりやすい名前にした方が良い let cellData = sectionData[cellText] //<-1 // チェック状態が true なら、初めからチェック状態にする //###Swiftの条件節は`Bool`だけを受け付けるので普通は`== true`とは書かないが、`cellData`は`Bool?`型なのでこの書き方が便利 if cellData == true { cell.imageView?.image = UIImage(named: "checked") } else { cell.imageView?.image = UIImage(named: "unchecked") } return cell } // セルがタップされた時の処理 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if let cell = tableView.cellForRow(at: indexPath) { // タップしたセルのテキストを取得 //###`Optional`のままでは使いづらいので`??`演算子でデフォルト値を与えておく let cellText = cell.textLabel?.text ?? "" // 画像を切り替えと Dictonary の値を変更 if cell.imageView?.image == UIImage(named: "checked") { //###`tableData`の何番目のデータを更新するのか選ばないといけない //###辞書型の値の更新は、通常subscriptで行う self.tableData[indexPath.section][cellText] = false cell.imageView?.image = UIImage(named: "unchecked") //<-2 } else { //###`tableData`の何番目のデータを更新するのか選ばないといけない //###辞書型の値の更新は、通常subscriptで行う self.tableData[indexPath.section][cellText] = true cell.imageView?.image = UIImage(named: "checked") //<-2 } // 選択状態を解除 cell.isSelected = false } } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { let sectionData = tableData[section] //###`tableData`の要素数はnumberOfRowsではなく、`numberOfSections`として使う //⇒`tableData`の要素数は`sectionTitle`の要素数と同じでないといけない return sectionData.count } ``` 他にもまだ細かい部分で別のエラーが出るかもしれませんが、とりあえず上記のような修正で、今回ご質問に挙げられたようなエラは出なくなると思います。お試しください。