iOSエンジニアのつぶやき

毎朝8:30に iOS 関連の技術について1つぶやいています。まれに釣りについてつぶやく可能性があります。

UICollectionView の automaticSize に気をつけよう

UICollectionView を使用して、Cell の幅などを動的にしたい時って結構ありますよね。そんな時は、下記のように estimatedItemSizeUICollectionViewFlowLayout.automaticSize を使用する場合が多いかと思います。

flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

これは iOS10 以降から使用できるグローバル変数なのですが、この記事でも記載されている通り、iOS10 ではすごくバグが多くてあまり使えなかったみたいですが、この現象は iOS11 以降でもちらほら見られることがあったので、そのような場合の具体的な改善策を今回は残しておこうかと思います。

※ 今回のまとめは横スクロールの CollectionView に関するものです

具体的に発生した問題

  • 初回表示時に Cell がなぜかスクエアで表示されて、スクロールするとコンテンツの幅に合わせて正しい Cell のサイズになおる(iOS12.1)
  • 表示されない Cell がある(iOS12系)
  • 1つ目の Section と、2つ目の Section の Cell が重なって表示される(iOS13系)
  • 更新アニメーションが変な動きをする(iOS11系)
    • Cell のサイズを正しく計算できていないのが原因と考えられる

改善策

具体的な改善策は下記の2つです。

  1. estimatedItemSize に UICollectionViewFlowLayout.automaticSize をセットするのではなく、FlowLayot の Delegate を使用して ItemSize を決定する。
  2. systemLayoutSizeFitting を活用して AutoLayout後の Cell の正しいサイズを取得する。

では、実際にコードを書いていきましょう。Cell のサイズを正しく取得するために、ダミーの Cell インスタンスを取得するためのプロパティを作成しておきます。

    private lazy var cellHelper: HogeCollectionViewCell? = {
        let object = UINib(nibName: "HogeCollectionViewCell", bundle: nil).instantiate(withOwner: nil).first
        return object as? HogeCollectionViewCell
    }()

作成した ダミー Cell に実際に使用する Text などをセットして、サイズを取得できるようにします。systemLayoutSizeFitting で制約に基づいた Cell の正しいサイズを取得して、Delegate で返せば正しく Cell が表示されるようになります🎉 UIView.layoutFittingCompressedSize を設定することで、制約の範囲で最も小さいサイズが返されるようになります。制約の範囲で最も大きいサイズを返したい場合は layoutFittingExpandedSize を使用します。

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let dataSource = viewModel.dataSources[indexPath.section]
        let data = dataSource.elements[indexPath.row]
        if let cell = cellHelper {
            cell.set(iconImageUrl: data.imageUrl, amount: data.amount, teamColor: data.teamColor)
            return cell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
        }
        return .zero
    }

以前にも、CollectionView のバグに関する記事を書きましたが、FlowLayout 周りのところは iOS13 でもちらほらバグがあるので、基本的には FlowLayoutDelegate を使用した方が全体的にいい気がしました。また、今回のバグについてはどのような状況で発生しているのかが要素が多すぎて判断しきれなかったので、わかり次第記載しようかと思います。(個人的には、横スクロール時にこのようなバグが発生するケースが多かったので、width を動的に変更する場合などが関係しているのではという気もしています🤔)

yamato8010.hatenablog.com

参考

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com