Instagram / Instagram/IGListKit
Section Controllers with multiple cells, horizontally laid out
- Dominant language
- Objective-C
- Stars
- 13.1k
- Forks
- 1.5k
- PR merge metrics
- No merged PRs in 30d
Description
## New issue checklist
- [x] I have reviewed the [`README`](https://github.com/Instagram/IGListKit/blob/master/README.md) and [documentation](http://instagram.github.io/IGListKit)
- [x] I have searched [existing issues](https://github.com/Instagram/IGListKit/issues) and this is not a duplicate
### General information
- `IGListKit` version:
- iOS version(s): 11.3
- CocoaPods/Carthage version:
- Xcode version: 9.3
- Devices/Simulators affected: N/A
- Reproducible in the demo project? (Yes/No): N/A
- Related issues: not found
Hi IG team,
First of all, thank you for sharing this framework, I've been using it extensively for a few months and it has simplified my life drastically. So far I've always been able to implement the UX I wanted by doing research and looking at the examples you provided, but I finally hit a wall this week.
As illustrated on the drawing below, I need to create a nested section that scrolls horizontally and hosts a bunch of section controllers, each containing 2 cells. What I am not managing to do is have the 2 cells be laid out next to each other horizontally instead of stacked on top of each other, in each (blue) section controller.
I am using IGListkit's ListCollectionViewLayout for the nested collection view, which allows me to have the blue section controllers nicely laid out horizontally as shown on the drawing, but cannot get the 2 cells not to be stacked on top of each other.
I am attaching a simplified version of the code, any guidance on how to this would be most appreciated.
Thanks again!

**Simplified code for the blue section controller**
```
class BlueSectionController : ListSectionControllerBase{
//Space between pods
static let customInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
static let sectionHeight : CGFloat = OrangeCell.cellHeight + BlueSectionController.customInsets.top + BlueSectionController.customInsets.bottom
var viewModel : ViewModel?
override init() {
super.init()
inset = BlueSectionController.customInsets
}
override func numberOfItems() -> Int {
return 2
}
override func sizeForItem(at index: Int) -> CGSize {
guard let context = collectionContext else {return .zero}
switch index{
case 0: //Orange cell (cell 1)
return CGSize(width: OrangeCell.cellWidth, height: OrangeCell.cellHeight)
default: //Green cell (cell 2)
let width = context.containerSize.width - OrangeCell.cellWidth - 50 //50 = space on the right to allow next section to tip in
return CGSize(width: width, height: GreenCell.cellHeight) //Both cells have the same height
}
}
override func cellForItem(at index: Int) -> UICollectionViewCell {
guard let context = collectionContext else {return UICollectionViewCell()}
switch index{
case 0: //Orange cell (cell 1)
guard let cell = context.dequeueReusableCell(withNibName: "OrangeCell", bundle: nil, for: self, at: index) as? OrangeCell else {return UICollectionViewCell()}
//Configure cell...
return cell
default: //Green cell (cell 2)
guard let cell = context.dequeueReusableCell(withNibName: "GreenCell", bundle: nil, for: self, at: index) as? GreenCell else {return UICollectionViewCell()}
//Configure cell...
return cell
}
}
//This function is used to hand an object to the section controller. Note this method will always be called before any of the cell protocol methods.
override func didUpdate(to object: Any) {
guard let viewModel = object as? ViewModel else {return}
self.viewModel = viewModel
}
}
```
**Simplified code for the nested section controller**
```
class NestedSectionController: ListSectionController{
final lazy var listAdapter: ListAdapter = { [unowned self] _ in
let adapterUpdater = ListAdapterUpdater()
//adapterUpdater.delegate = self
let adapter = ListAdapter(updater: adapterUpdater, viewController: self.viewController)
return adapter
}()
//Space between this section and the next
static let customInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
var viewModelArray : [ViewModel]?
var
override init() {
super.init()
listAdapter.dataSource = self
}
override func numberOfItems() -> Int {
return 1
}
override func didUpdate(to object: Any) {
guard let nestedSectionToken = object as? NestedSectionToken else { return }
self.viewModelArray = nestedSectionToken.viewModelArray
}
override func didSelectItem(at index: Int) {
}
override func sizeForItem(at index: Int) -> CGSize {
guard let context = collectionContext else {return .zero}
return CGSize(width: context.containerSize.width, height: BlueSectionController.sectionHeight * 2)
}
override func cellForItem(at index: Int) -> UICollectionViewCell {
guard let cell = collectionContext?.dequeueReusableCell(of: HorizontalEmbeddedCollectionViewCell.self, for: self, at: index) as? HorizontalEmbeddedCollectionViewCell else {return UICollectionViewCell()}
listAdapter.collectionView = cell.collectionView
listAdapter.scrollViewDelegate = self
listAdapter.collectionView?.reloadData() //Needed to avoid crash (see https://github.com/Instagram/IGListKit/issues/952)
return cell
}
}
extension NestedSectionController: ListAdapterDataSource {
func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
return arrayOfViewModels as [ListDiffable]
}
func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
guard let viewModel = object as? ViewModel else {return ErrorSectionController()}
let section = BlueSectionController()
//Configure section...
return section
}
func emptyView(for listAdapter: ListAdapter) -> UIView? {
return nil
}
}
```
**Code for the Embedded CollectionView cell**
```
final class HorizontalEmbeddedCollectionViewCell: UICollectionViewCell {
static let cellHeight : CGFloat = MyPodCellSectionController.sectionHeight
lazy var collectionView: UICollectionView = {
let layout = ListCollectionViewLayout(stickyHeaders: false, scrollDirection: .horizontal, topContentInset: 0, stretchToEdge: false)
let view = UICollectionView(frame: .zero, collectionViewLayout: layout)
view.showsHorizontalScrollIndicator = false
view.showsVerticalScrollIndicator = false
view.backgroundColor = .white
view.alwaysBounceVertical = false
view.alwaysBounceHorizontal = true
self.contentView.addSubview(view)
return view
}()
override func layoutSubviews() {
super.layoutSubviews()
collectionView.frame = contentView.bounds
}
}
```
Contributor guide
Assessment
This issue has not been assessed yet.