【Swift4.0】初心者向けにTableViewで複数セクションの表示と折りたたみをまとめてみた【iOS】

スポンサーリンク

テーブルビューの実装について、まとまった情報が無かったので、集約的な意図も込めてテーブルビューの基本的な実装をまとめました。

とりあえずサンプルが欲しいって人は下の方にあります。初心者の自分が作成したので、初心者向けです。

この記事でできること

  • テーブルビューの表示
  • 複数セクションでの表示
  • セクション単位のテーブルの折りたたみ
  • セルをスワイプしての削除
  • セルをタップして値を表示

バージョン

Swift : 4.2.1
Xcode : 10.1
iOS : 12.1
★2018/12/15時点のバージョンで動作確認したので修正。プログラムの修正は無し。(2018/12/15追記)



Storyboard

Storyboardの設定は「TableView」を配置しただけです。吹き出しの項目以外は今回は特に気にしてません。配置したTableViewを「myTableView」としてViewControllerに接続しています。

テーブルの表示

まず初めにテーブルの表示のためには、下記のデリゲートの設定が必要となります。

  • UITableViewDelegate
  • UITableViewDataSource
続いてテーブルを表示するために必要なメソッドを実装します。テーブルの表示するためには、メソッドが2つ必要になります。

  • テーブルビューに表示するセルの数を返すメソッド
  • テーブルビューに表示するセルを返すメソッド
セルの数を返す際には、DataSource(items1)の配列の要素数を返しています。

セルの返却では、「withIdentifier」にStoryboard作成時に入力したセルの識別子を指定しセルを取得後、items1の要素をセルの値(text)に設定しています。

ここまで実装した状態だとこうなります。

背景色はStoryboardでセルに設定しています。データが表示されていないところはセルが設定されていないため背景色はデフォルトになります。テーブルビュー全体に背景色を設定したい時には下記のようにすれば、色の指定ができます。

複数セクションの表示

続いて複数セクションの表示です。セクションの表示のためには下記のメソッドを用意します。

  • テーブルビューに表示するセクションの数を返すメソッド
  • テーブルビューに表示するセクションのタイトルを返すメソッド

また、上に書いたセルの数を返すメソッドはセクションごとにセルの数を返すように変更します。

テーブルの折りたたみ

セクションの表示ができたので、今度はセクション単位でセルを折りたたみできるようにします。
テーブルの折りたたみのためには、タップされたセクションを取得する必要があります。下記のような実装をします。

  • セクションのヘッダ部分にView設定する。
  • Viewにタップアクションを設定する。
  • タップされたセクションの表示セルの数を「値を表示してれば0」、「0だったらセルの数」として再読込する。

「セクションのタイトルを返す」メソッドについては、Viewを設定するメソッドが代替となるため不要になります。
サンプルはフラグでセクション毎の折りたたみ状態を制御しています。セクションをタップしたらフラグを変更して、再度セクションを読み込みます。

セルの削除とタップ

セルの表示が完了したので、最後はセルの削除とタップして値を表示します。これらもテーブルビューのメソッドを実装することで実現できます。

  • テーブルビューのセルをスワイプするアクションのメソッド
  • テーブルビューのセルを選択した時のメソッド

スワイプのアクションのメソッドでは、データソースの配列データを削除するとともにテーブルビューの列も削除しています。

セル選択ではセルの値を取得して、アラートで表示しています。


スポンサーリンク

サンプル

GitHub:Mitsuya-Daisa/FoldingTableView

参考にさせて頂きました

 

 

スポンサーリンク

19 件のコメント

  • こんにちは!
    cellForRowAtのコードですが、最初のコードでは
    cell.textLabel?.text = items1[indexPath.row] as? String
    になっているかと思いますが、複数セクションではどのように設定されていますか?

    • こんにちは!
      コメントありがとうございます!

      cellForRowAtについて複数セクションの対応がされていませんでした、、、すみません。
      sectionが複数になっているので、下記のようにループして一つずつ設定する形としています。

      for (value) in sections[indexPath.section].values
      {
      cell.textLabel?.text = value[indexPath.row] as? String
      }

      記事の方のコードも修正していますので、参考にしてください!

  • ありがとうございます!
    とりあえず、indexPath.section をifで対応していたのですが、数が固定されてしまうため困ってました!
    早速試してみます!

  • こんにちは!
    アプリ作成初心者でして、こちらの記事を参考にさせていただいておりました!

    写経していたのですが、一点どうしてもエラーが解除できないので、もしよろしければご教授願えればと思います。

    テーブルの折りたたみにおいて、セクションのビューにタップジェスチャーを設定する部分で、action: #selector(self.tapHeader(gestureRecognizer:))))のselfにtapHeader(gestureRecognizer:))))が無いよと言われてしまいます。

    何か足りないとは理解できるのですが、どこに何を追加すればいいのかがわかりません。
    よろしくお願いします!

    • こんにちは!
      コメントありがとうございます!

      エラーのメッセージはこんな感じでしょうか?
       →「Value of type ‘ViewController’ has no member ‘tapHeader(gestureRecognizer:)’」

      情報が少ないので推測になってしまいますが、コメント頂いたように「tapHeader(gestureRecognizer:)」が無いものと思われます。
      上から写経されているかもしれませんが、以下のメソッドは作成されているでしょうか?
      (記事内を検索してもらえれば出てくると思います。)

      @objc func tapHeader(gestureRecognizer: UITapGestureRecognizer) {
          (中略)
      }

      自分のソースで上のメソッドをコメントアウトしたら上記のエラーメッセージが出たので、今の所これが原因かなと思っています。
      お試しください!

  • こんにちわ
    Value of type ‘ViewController’ has no member ‘tapHeader(gestureRecognizer:)’  というエラーメッセージが出てしまったのですがどうすればいいでしょうか? 前の質問者様の回答にあったように
    @objc func tapHeader(gestureRecognizer: UITapGestureRecognizer) {
        (中略)
    のところを入れたのですが @objc can only be used with members of classes, @objc protocols, and concrete extensions of classes    Replace ‘@objc ‘ with ” と出たので
    func tapHeader(gestureRecognizer: UITapGestureRecognizer) { に書き換えました。 また
    guard let section = gestureRecognizer.view?.tag as Int! else { の部分も Using ‘!’ is not allowed here; perhaps ‘?’ was intended?  Replace ‘!’ with ‘?’ というメッセージが出たためguard let section = gestureRecognizer.view?.tag as Int? else { に書き換えました。
    これらのことをしてもエラーが消えなかったのですが
    どうすればいいでしょうか?

    • コメントありがとうございます!

      Value of type ‘ViewController’ has no member ‘tapHeader(gestureRecognizer:)’  というエラーメッセージが出てしまったのですがどうすればいいでしょうか? 前の質問者様の回答にあったように
      @objc func tapHeader(gestureRecognizer: UITapGestureRecognizer) {
          (中略)
      のところを入れたのですが @objc can only be used with members of classes, @objc protocols, and concrete extensions of classes    Replace ‘@objc ‘ with ” と出たので

      @objc func tapHeader(gestureRecognizer: UITapGestureRecognizer) の追加位置がクラスの外になっていることが原因と思われます。
      自分の環境でtapHeaderをクラスの外に移動してみたら同じ「@objc can only be used with members of classes, ・・・」のエラーメッセージが表示されました。
      {}の位置に注意して、tapHeaderの位置を見直してみてください。


      class ViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {
       (中略)
       「@objc func tapHeader(gestureRecognizer: UITapGestureRecognizer) 」 ←クラスの中に入るようにする。(OK)
      }
       「@objc func tapHeader(gestureRecognizer: UITapGestureRecognizer) 」 ←ここだとエラーになる(NG)

      上記で解決すれば、以下の対応は不要かと思います。

      Replace ‘@objc ‘ with ” と出たので
      func tapHeader(gestureRecognizer: UITapGestureRecognizer) { に書き換えました。 また
      guard let section = gestureRecognizer.view?.tag as Int! else { の部分も Using ‘!’ is not allowed here; perhaps ‘?’ was intended?  Replace ‘!’ with ‘?’ というメッセージが出たためguard let section = gestureRecognizer.view?.tag as Int? else { に書き換えました。

      お試しください!

      • すみません、どこに{}を追加すれば良いのかわからないです、、、
        今はサンプルの通り
        @objc func tapHeader(gestureRecognizer: UITapGestureRecognizer) {
        // タップされたセクションを取得する。
        guard let section = gestureRecognizer.view?.tag as Int! else {
        return
        }
        と書かれている状況なのですが、どうすれば良いのでしょうか。
        何回もすみません、、

        • {}を追加するのではなく、既にあるclassの中にtapHaederが記載されているかをご確認ください。コードの不足というよりも記載位置の問題かと思われます。
          以下のような階層で記載されていればエラーにならないはずです。

          class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
          (中略)
           @objc func tapHeader(gestureRecognizer: UITapGestureRecognizer) {
            (中略)
           }
          }

          • 記載位置を確認しましたがエラーが消えないです。現在の画面の状況を伝えるためにTwitterのdmで画像を送らせていただきました。
            確認していただけると幸いです。

  • DMありがとうございました。
    DMの方でも返信させていただきましたが、他のメソッド(numberOfSections)の閉じカッコが無いことが原因と思われます。ご確認ください。

  • 本稿を参考にしているアプリ作成初心者です。
    itemsの配列が、ある程度固定化されていますが、表示するデータ数を可変化したいのですが可能でしょうか。また、section数もある程度増やしたいと思っています。単純にsection数を増やすだけで大丈夫でしょうか。

    • コメントありがとうございます!

      「可変化」というのを初期表示した後に何かしらの操作でデータやsection数を変更したいと認識しての回答です。
      現在swiftの開発環境がないため検証できていませんが、表示するデータの可変化は配列の中身を変えてViewの再読み込みのようにすればできるように思います。
      section数についても同様ですが、サンプルのswitch文を使っている箇所は予めsection数が決まっていることが前提で書いているので、この部分は手直しが必要と思われます。

      単純に初期表示のデータ数やsection数を増やしたいということであれば、サンプルをベースに配列増やしたり、switch文のcaseを増やせば可能です。

      • 「可変化」と表現したのは、表示する内容が最初から決まっておらず、具体的にはディレクトリ内のファイルを表示する場合を想定しています。一応、10個としてテストしている状態です。
        【var items1: NSMutableArray = [“xxx”, “yyy”, “zzz”]】の配列を不特定多数に出来ればと思いますが・・・。自分でも調べていますが何らか手法はないでしょうか。
        別件になりますが、セルの削除を実行するとシュミレーションが停止してしまいます。元々のソースコードでは問題ないのですが、itemsやsection数を増やした事によるものなのかわからないのですが、いろいろをテストしている状態です。原因追及の何らかのアドバイスを頂ければ助かります。

        • しばらくswiftから離れており記事の範囲を超える内容となると自分の方では推測程度の回答しかできないためテラテイル等のサイトで質問することをおすすめいたします。
          実現したいこと、該当箇所のソースコード、発生しているエラーの内容を明記すれば回答がきやすいと思います。
          お力になれず申し訳ありませんが、よろしくおねがいします。

  • コメントを残す

    メールアドレスが公開されることはありません。 * が付いている欄は必須項目です