iosswiftiglistkit

How to create a two column collection view list with IGListKit in swift


I am using IGListKit library(IGListKit). I would like to create a two column collection view list like below. I have read the IGListKit manual, but I couldn't understand how to achieve that.

I passed the proper width in sizeForItem, but the column becomes 1.

Could you give me any advice ?

enter image description here

Additional Info

Following is my code and its output screenshot.

First code is ViewController.

class ViewController: UIViewController {
    lazy var adapter: ListAdapter = {
        return ListAdapter(updater: ListAdapterUpdater(), viewController: self)
    }()

    @IBOutlet weak var collectionView: UICollectionView!

    let channels = [
        Channel(id: "1", color: UIColor.black),
        Channel(id: "2", color: UIColor.blue),
        Channel(id: "3", color: UIColor.red),
        Channel(id: "4", color: UIColor.yellow),
        Channel(id: "5", color: UIColor.green),
        ]

    override func viewDidLoad() {
        super.viewDidLoad()

        self.adapter.collectionView = self.collectionView
        self.adapter.dataSource = self
    }

    func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
        return self.channels as! [ListDiffable]
    }

    func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
        return ChannelSectionController()
    }

    func emptyView(for listAdapter: ListAdapter) -> UIView? {
        return nil
    }

Following is SectionController.

final class ChannelSectionController: ListSectionController {

    var channel: Channel?

    override func numberOfItems() -> Int {
        return 1
    }

    override func sizeForItem(at index: Int) -> CGSize {
        let length = collectionContext!.containerSize.width / 4
        return CGSize(width: length, height: length)
    }

    override func cellForItem(at index: Int) -> UICollectionViewCell {
        guard let channel = channel else {
            fatalError("channel is nil.")
        }

        guard let cell = self.collectionContext?.dequeueReusableCellFromStoryboard(withIdentifier: "ChannelCell", for: self, at: index) as? ChannelCollectionViewCell else {
            fatalError()
        }
        cell.channel = channel // Update the cell label and color.
        return cell
    }

    override func didUpdate(to object: Any) {
        self.channel = object as? Channel
    }

    override func didSelectItem(at index: Int) {}
}

As you can see, width which is returned in sizeForItem() is enough small than frame width. However, the output column becomes one line.

enter image description here


Solution

  • Based on this article(at the bottom) how to create an object you should create your object like this:

    class ChannelCollection: ListDiffable {
    
    var id : String
    var channels: [Channel]
    init(id: String,channels: [Channel]) {
        self.id = id
        self.channels = channels
    }
    func isEqual(toDiffableObject object: ListDiffable?) -> Bool {
        return true //compare your object here, I returned true for test
    }
    func diffIdentifier() -> NSObjectProtocol {
        return id as NSObjectProtocol
    }
    }
    

    and your viewcontroller should look like this:

    class ViewController: UIViewController,ListAdapterDataSource {
    lazy var adapter: ListAdapter = {
        return ListAdapter(updater: ListAdapterUpdater(), viewController: self)
    }()
    var items : [Any] = []
    @IBOutlet weak var collectionView: UICollectionView!
    var channelCollection : ChannelCollection?
    let channels = [
        Channel(id: "1", color: UIColor.black),
        Channel(id: "2", color: UIColor.blue),
        Channel(id: "3", color: UIColor.red),
        Channel(id: "4", color: UIColor.yellow),
        Channel(id: "5", color: UIColor.green),
        ]
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        collectionView.frame = view.bounds
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.channelCollection = ChannelCollection.init(id: "1", channels: self.channels)
        self.items.append(self.channelCollection as Any)
        self.adapter.collectionView = self.collectionView
        self.adapter.dataSource = self
    }
    
    func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
        return self.items as! [ListDiffable]
    }
    
    func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
        return ChannelSectionController.init(channelCollection: self.channelCollection!)
    }
    
    func emptyView(for listAdapter: ListAdapter) -> UIView? {
        return nil
    }}
    

    your ListSectionController:

    final class ChannelSectionController: ListSectionController {
    
    var channel: Channel?
    
    var channelCollection : ChannelCollection?
    init(channelCollection: ChannelCollection)
    {
        self.channelCollection = channelCollection
    }
    override func numberOfItems() -> Int {
        return (self.channelCollection?.channels.count)!
    }
    
    override func sizeForItem(at index: Int) -> CGSize {
        let length = collectionContext!.containerSize.width / 4
        return CGSize(width: length, height: length)
    }
    
    override func cellForItem(at index: Int) -> UICollectionViewCell {
    
    
        guard let cell = self.collectionContext?.dequeueReusableCellFromStoryboard(withIdentifier: "ChannelCell", for: self, at: index) as? ChannelCollectionViewCell else {
            fatalError()
        }
        //cell.channel = channel // Update the cell label and color.
        return cell
    }
    
    override func didUpdate(to object: Any) {
        self.channel = object as? Channel
    }
    
    override func didSelectItem(at index: Int) {}}
    

    So what I changed here is that you are adding 5 sections,better practice is to add a single section and 5 rows,this code outputs the thing you want,just try it.