To implement a list view, using the official Apple way is disgusting. It is impossible to implement multiple proxies and duplicate functions with the same name and long parameter names.

Then, according to the Android Adapter, make a Swift version of the Adapter class, convenient implementation of all these, first look at the use of the method

Simple to use

Implement a simple UICollectionView list view:

class MyModel{
    / / data model
}
class MyViewCell: UICollectionViewCell {
    // Create Cell layout
}
// Create an Adapter class, passing in the generic model and cell
class MyAdapter: BaseQuickAdapter<MyModel.MyViewCell> {
    / / cell size
    override func cellSize(data:MyModel.indexPath:IndexPath) ->CGSize{
        CGSize(width:ScreenWidth, height:PT_100)}override func bindView(data:MyModel.indexPath:IndexPath.cell:MyViewCell) {
        // Display model data on cell}}Copy the code

Then bind:

        let mCollectionView = UICollectionView(frame: .zero ,collectionViewLayout: UICollectionViewFlowLayout())
        let mAdapter = MyAdapter(a)// Bind collectionView and Adapter
        mCollectionView.setAdapter(mAdapter)
Copy the code

Call it a day!

Use the advanced

        mAdapter.setOnItemSelected { (indexPath, model) in
            // Handle the click event
        }
        mAdapter.setOnItemLongPress { (indexPath, model) in
            // Handle the long press event
            
            // Remove data
            mAdapter.removeItem(indexPath)
        }
        // Set the data
        let data = [MyModel(),MyModel(),MyModel()]
        mAdapter.setNewData(data)
        // Add data
        mAdapter.appendData(MyModel())
        // Get adapter data
        let dataArray = mAdapter.getData()
Copy the code

Use multiple cell layouts

Again, very simple


// Data model implements the CellType protocol and distinguishes the cell types to be used according to the CellType attribute. CellType is the identifier used when registering cells
class MyModel:CellType{
    static let TYPE_1 = "1"
    static let TYPE_2 = "2"
    
    // Set different types for different layouts
    var cellType: String = TYPE_1
}
class MyViewCell: UICollectionViewCell {
    // Create Cell layout
}
class MyViewCell2: UICollectionViewCell {
    // Create Cell layout
}
// Create an Adapter class
class MyAdapter: BaseAdapter<MyModel> {
    override init(a) {
        super.init(a)/ / register cell
        registerCell(MyViewCell.self.MyModel.TYPE_1)
        registerCell(MyViewCell2.self.MyModel.TYPE_2)}/ / cell size
    override func cellSize(data: MyModel.indexPath: IndexPath) -> CGSize {
        // Set different sizes according to data.cellType
        if data.cellType = = MyModel.TYPE_1 {
            return CGSize(width: ScreenWidth, height: PT_100)}else if data.cellType = = MyModel.TYPE_2{
            return CGSize(width: ScreenWidth, height: PT_200)}return .zero
    }
    override func bindView(data: MyModel.indexPath: IndexPath.collectionCell: UICollectionViewCell) {
        // Display model data on cell
        
        // Convert to different cells according to data.cellType
        if data.cellType = = MyModel.TYPE_1 {
            let cell = collectionCell as! MyViewCell
        }else if data.cellType = = MyModel.TYPE_2{
            let cell = collectionCell as! MyViewCell2}}}Copy the code

At present, this function is done. If other functions are used, it will be added later. Beginner, the code is relatively simple and optional O(∩_∩)O, welcome correction!

BaseAdapter source code:

//
// BaseAdapter.swift
// bookista
//
// Created by DU on 2021/4/25.
//

import Foundation

let DefaultCellId = "BaseCell" // cellId by default

class BaseAdapter<T> :NSObject.UICollectionViewDataSource.UICollectionViewDelegate.UICollectionViewDelegateFlowLayout.UITableViewDelegate.UITableViewDataSource{
    
    private var mData = [T] ()// Data list
    private var mCell = [String:AnyClass? ] (a)// The cell to be registered
    private var onItemSelected:((IndexPath.T) - >Void)? // Click the event
    private var onItemLongPress:((IndexPath.T) - >Void)? // Long press event
    
    private var mCollectionView:UICollectionView?
    private var mTableView:UITableView?
    
    /** Bind UICollectionView */
    func bindTo(_ collectionView: UICollectionView){
        if mTableView ! = nil || mCollectionView ! = nil {
            fatalError("Already bound to a UITableView or UICollectionView.")
        }
        mCollectionView = collectionView
        mCollectionView?.delegate = self
        mCollectionView?.dataSource = self
        mCell.forEach { (cell) in
            mCollectionView?.register(cell.value, forCellWithReuseIdentifier: cell.key)
        }
    }
    /** Bind UITableView */
    func bindTo(_ tableView: UITableView){
        if mTableView ! = nil || mCollectionView ! = nil{
            fatalError("Already bound to a UITableView or UICollectionView.")
        }
        mTableView = tableView
        mTableView?.delegate = self
        mTableView?.dataSource = self
        mCell.forEach { (cell) in
            mTableView?.register(cell.value, forCellReuseIdentifier: cell.key)
        }
    }
    
    /** Get data */
    func getData(a)- > [T] {return mData
    }
    /** Set new data */
    func setNewData(_ data: [T]){
        mData.removeAll()
        mData.append(contentsOf: data)
        mCollectionView?.reloadData()
        mTableView?.reloadData()
    }
    /** Add data */
    func appendData(_ data:T){
        mData.append(data)
        mCollectionView?.reloadData()
        mTableView?.reloadData()
    }
    
    /** Register cell - Register reuseIdentifier = model.cellType */
    func registerCell(_ cell:AnyClass? ._ reuseIdentifier: String){
        mCell.updateValue(cell, forKey: reuseIdentifier)
    }
    /** Register cell - Only one style */
    func registerCell(_ cell:AnyClass?).{
        self.registerCell(cell, DefaultCellId)}/** item click */
    func setOnItemSelected(_ onItemSelected: ((IndexPath.T) - >Void)?){
        self.onItemSelected = onItemSelected
    }
    
    /** Set the long click event */
    func setOnItemLongPress(_ todo: ((IndexPath.T) - >Void)?){
        self.onItemLongPress = todo
        let recognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressGesture(_:)))
        self.mCollectionView?.addGestureRecognizer(recognizer)
        self.mTableView?.addGestureRecognizer(recognizer)
    }
    
    // Long press action
    @objc func longPressGesture(_ tap: UILongPressGestureRecognizer) {
        if tap.state = = .began {
            if mTableView ! = nil {
                guard let path = mTableView!.indexPathForRow(at: tap.location(in: mTableView!)) else {
                    return
                }
                self.onItemLongPress?(path,mData[path.row])
            }else if mCollectionView ! = nil {
                guard let path = mCollectionView!.indexPathForItem(at: tap.location(in: mCollectionView!)) else {
                    return
                }
                self.onItemLongPress?(path,mData[path.item])
            }
        }
    }
    /** Remove cell */
    func removeItem(_ indexPath:IndexPath){
        if mCollectionView ! = nil {
            mData.remove(at: indexPath.item)
            mCollectionView?.deleteItemsAtIndexPaths([indexPath], animationStyle: .automatic)
        }else if mTableView ! = nil {
            mData.remove(at: indexPath.row)
            mTableView?.deleteItemsAtIndexPaths([indexPath], animationStyle: .automatic)
        }
    }
    
    
    //MARK: UITableView =========================================================
    
    /** Set the view to display the content - UITableViewCell */
    func bindView(data:T.indexPath: IndexPath.tableCell:UITableViewCell){}/** table cell height */
    func cellHeight(data:T.indexPath: IndexPath) -> CGFloat{
        return 100
    }
    func tableView(_ tableView: UITableView.numberOfRowsInSection section: Int) -> Int {
        mData.count
    }
    
    func tableView(_ tableView: UITableView.cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var cellId = DefaultCellId
        if T.self is CellType {
            cellId = (mData[indexPath.row] as! CellType).cellType
        }
// let cellId = mData[indexPath.row].cellType
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
        self.bindView(data: mData[indexPath.row], indexPath: indexPath, tableCell: cell)
        return cell
    }
    func tableView(_ tableView: UITableView.didSelectRowAt indexPath: IndexPath) {
        self.onItemSelected?(indexPath,mData[indexPath.row])
    }
    func tableView(_ tableView: UITableView.heightForRowAt indexPath: IndexPath) -> CGFloat {
        return cellHeight(data: mData[indexPath.row], indexPath: indexPath)
    }
    
    
    
    //MARK: UICollectionView =========================================================
    
    /** Set the view to display the content - UICollectionViewCell */
    func bindView(data:T.indexPath: IndexPath.collectionCell:UICollectionViewCell){}/** cell size */
    func cellSize(data:T.indexPath: IndexPath) -> CGSize{
        return .zero
    }
    /** Line spacing */
    func lineSpace(a) -> CGFloat{
        0
    }
    /** Left and right spacing */
    func interitemSpace(a) -> CGFloat{
        0
    }
    /** section margin */
    func sectionInset(a) -> UIEdgeInsets {
        .zero
    }
    
    
    func collectionView(_ collectionView: UICollectionView.numberOfItemsInSection section: Int) -> Int {
        mData.count
    }
    func collectionView(_ collectionView: UICollectionView.cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        var cellId = DefaultCellId
        if T.self is CellType {
            cellId = (mData[indexPath.item] as! CellType).cellType
        }
// let cellId = mData[indexPath.item].cellType
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
        self.bindView(data: mData[indexPath.item], indexPath: indexPath, collectionCell: cell)
        return cell
    }
    func collectionView(_ collectionView: UICollectionView.didSelectItemAt indexPath: IndexPath) {
        self.onItemSelected?(indexPath,mData[indexPath.item])
    }
    
    func collectionView(_ collectionView: UICollectionView.layout collectionViewLayout: UICollectionViewLayout.sizeForItemAt indexPath: IndexPath) -> CGSize {
        return cellSize(data:mData[indexPath.item],indexPath: indexPath)
    }
    
    func collectionView(_ collectionView: UICollectionView.layout collectionViewLayout: UICollectionViewLayout.minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return lineSpace()
    }
    func collectionView(_ collectionView: UICollectionView.layout collectionViewLayout: UICollectionViewLayout.minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return interitemSpace()
    }
    func collectionView(_ collectionView: UICollectionView.layout collectionViewLayout: UICollectionViewLayout.insetForSectionAt section: Int) -> UIEdgeInsets {
        return sectionInset()
    }
    
}
//MARK: BaseQuickAdapter =================================

/** Quickly create a single-style adapter */
class BaseQuickAdapter<Model.ViewCell:UIView> :BaseAdapter<Model> {
    override init(a) {
        super.init()
        registerCell(ViewCell.self)}func bindView(data: Model.indexPath: IndexPath.cell: ViewCell){}override func bindView(data: Model.indexPath: IndexPath.tableCell: UITableViewCell) {
        self.bindView(data: data, indexPath: indexPath, cell: tableCell as! ViewCell)}override func bindView(data: Model.indexPath: IndexPath.collectionCell: UICollectionViewCell) {
        self.bindView(data: data, indexPath: indexPath, cell: collectionCell as! ViewCell)}}//MARK: ===============================================
extension UICollectionView {
    func setAdapter<T> (_ adapter:BaseAdapter<T>){
        adapter.bindTo(self)}}extension UITableView {
    func setAdapter<T> (_ adapter:BaseAdapter<T>){
        adapter.bindTo(self)}}/** The model class implements this protocol */
protocol CellType{
    /** ReuseIdentifier */ for cell registration
    var cellType:String{get set}}Copy the code