9
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

GenericなUITableViewCellを使う

Last updated at Posted at 2020-05-11

P.S この記事の内容は古くなりました。

iOS14にて、よく利用するセルのパターンとそのカスタマイズのためにUIContentConfigurationというオブジェクトが提供されました。

まずそちらを参考にしてください。

まえがき

TwitterのTLで少し前に**「UITableViewCellのサブクラスを毎回作るのではなく、GenericsなTableViewCellにUIViewを載せて対応する」**という方針を採用されておられる方がいて、良いなぁと思ったので作ってみました。

やったこと

GenericなUITableViewCellをつくる/つかう

GenericCell.swift
@dynamicMemberLookup public class GenericTableViewCell<View : UIView>: UITableViewCell {
    
    public let customView: View = .init(frame: .zero)
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        commonInit()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        
        commonInit()
    }

    public subscript<U>(dynamicMember keyPath: ReferenceWritableKeyPath<View, U>) -> U {
        get {
            customView[keyPath:keyPath]
        }
        
        set {
            customView[keyPath:keyPath] = newValue
        }
    }
    
    private func commonInit() {
        contentView.addSubview(customView)
        
        customView.translatesAutoresizingMaskIntoConstraints = false
        customView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
        customView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
        customView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
        customView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
    }

}

この構造のメリット

UITableViewCellをサブクラスしてViewを構築していく時の問題として、例えば表示内容は全く同じなのにUICollectionViewを利用した別の画面を作る必要が出た時に移行がすごく面倒です。

なので、普段からUIViewベースでViewを作成してGenericなTableViewCellに載せる構成にしておくことで、プロジェクトの変更・拡張に対してロバストです。

この構造のデメリット

Viewスタックが一層余分に増え、そのままではアクセスもcustomViewプロパティ経由になるので少し冗長になります。

なので、後者の問題を消すためにdynamicMemberLookupを採用し、直接内部Viewへのアクセス可能にしています。

使い方

TableViewへのセルの登録

Register.swift
tableView.register(GenericTableViewCell<YourCustomView>.self, forCellReuseIdentifier: "Cell")

セルの表示

Use.swift
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! GenericTableViewCell<YourCustomView>

CollectionViewCellでは?

CollectionViewCellでも同じような仕組みを作る事ができます。実装はほぼ変わりません。

追記

コメントで頂いたのですが、この記事では

public let customView: View = .init(frame: .zero)

としていますが、これではNib経由でのUIViewのインスタンス化が出来ません。

Nib経由でのインスタンス化に対する柔軟性が欲しい場合は、<View: UIView>とせずに生成は別の具体型に任せるFactoryMethodのようなデザインにすると良いでしょう。

詳しくはコメント欄をチェックしてみてください!

9
4
3

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
9
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?