P.S この記事の内容は古くなりました。
iOS14にて、よく利用するセルのパターンとそのカスタマイズのためにUIContentConfigurationというオブジェクトが提供されました。
まずそちらを参考にしてください。
まえがき
TwitterのTLで少し前に**「UITableViewCellのサブクラスを毎回作るのではなく、GenericsなTableViewCellにUIViewを載せて対応する」**という方針を採用されておられる方がいて、良いなぁと思ったので作ってみました。
やったこと
GenericなUITableViewCellをつくる/つかう
@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へのセルの登録
tableView.register(GenericTableViewCell<YourCustomView>.self, forCellReuseIdentifier: "Cell")
セルの表示
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のようなデザインにすると良いでしょう。
詳しくはコメント欄をチェックしてみてください!