LoginSignup
1
0

More than 1 year has passed since last update.

UITableViewにタグを並べて表示するようなセルを持たせる(画面回転対応版)

Last updated at Posted at 2021-07-13

概要

UITableViewでUIを組んでいて、例えば検索キーワードなどをタグのUIにして並べるようなときに使えるテクニックを説明します。
タグUIの実装は、isScrollEnabledをfalseにしたUICollectionView で実装しています。
これはあくまで一例なので、もっといい方法があればぜひコメントいただければと思います。

気をつける点

ただUICollectionViewのUIをTableViewに持たせるだけなのであれば比較的簡単かと思われますが、iOSは画面回転などでCollectionViewの行数が変わり、当然CollectionViewのサイズ自体が変わります。
iPadに関してはSplit Viewでの表示やSlide Overでの表示でもCollectionViewサイズが変わります。
それらのUIの各種変更にも対応する形で実装していきます。

完成図

全体がUITableViewで、UITableViewの中にUICollectionView持ちのセルが含まれています。

Simulator Screen Shot - iPod touch (7th generation) - 2021-07-13 at 17.30.34.png Simulator Screen Shot - iPod touch (7th generation) - 2021-07-13 at 17.30.24.png

UICollectionViewを持つUITableViewCellの実装

タグ表示用CollectionViewを持つUITableViewCellを実装します。
以下は実装のポイントです。

必要なプロパティの準備

以下のプロパティを持ちます。

    private var observer: NSKeyValueObservation?
    var collectionView: UICollectionView!

左寄せ用のUICollectionViewFlowLayoutの用意

CollectionViewに左寄せ用のUICollectionViewFlowLayoutを適用します。
https://stackoverflow.com/questions/22539979/left-align-cells-in-uicollectionview
有志が各種左寄せ用レイアウトを用意しているので、それを使いましょう。
このとき、CollectionViewFlowLayoutのestimatedItemSizeUICollectionViewFlowLayout.automaticSizeに設定しておくことを忘れないでください。

systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority)のオーバーライド

func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize
をオーバーライドして、collectionView.collectionViewLayout.collectionViewContentSizeを返します。

    override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
        return collectionView.collectionViewLayout.collectionViewContentSize
    }

TableViewのcellForRowAt用のデータ処理時にcontentSizeのobserveを作成する

例えばconfigure などのデータ処理用メソッドを用意して、func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell の実行時に呼び出した場合、configure内部でobserverを作成し、その後データ反映処理を行います。

observerはCollectionViewのContentSizeが変化するたびに動作します。
ContentSizeが変わったら、CollectionViewを最新の高さに対応するため、tableViewのreloadData()をコールします。

このとき、tableViewのcellForRowAtは仕組み上多重に動作するので、データをチェックして、違うデータの時のみCollectionViewのreloadData()を実行します。

    private var array: [String] = []

    func configure(array: [String]) {
        observer = collectionView.observe(\.contentSize, options: [.new]) { collectionView, change in
            if (change.newValue?.height) != nil {
                // コンテンツのサイズが画面回転やデータによって変更するたび、TableViewを再描画する
                self.tableView?.reloadData()
            }
        }
        // データが前回のものと同じ場合、collecviewViewのreloadDataを行わない
        if self.array != array {
            self.array = array
            collectionView.reloadData()
        }
    }

    // UICollectionViewDataSource
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        array.count
    }


// https://stackoverflow.com/questions/15711645/how-to-get-uitableview-from-uitableviewcell
extension UITableViewCell {
    var tableView: UITableView? {
        var view = superview
        while let v = view, v.isKind(of: UITableView.self) == false {
            view = v.superview
        }
        return view as? UITableView
    }
}

prepareForReuse のときにobserverをnilにしておきます。

    override func prepareForReuse() {
        super.prepareForReuse()
        observer = nil
    }

まとめ

この記事では、タグ表示用のCollectionView入りUITableViewCellに対して全ての実装を行うことで実現を行なっていますが、ポイントとしては2点あり、

  • systemLayoutSizeFittingcollectionView.collectionViewLayout.collectionViewContentSize を返す
  • collectionView.contentSizeが変わったことを何らかの方法で検知し、TableView.reloadData() を行う

ということができれば、今回の実装に拘らなくともタグを並べての表示が可能かと思います。

1
0
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
1
0