Swift中使用IGListKit创建双列Collection View列表的问题
解决IGListKit双列Collection View显示为单列的问题
看起来你遇到的核心问题是每个Channel对象对应了一个独立的ListSectionController,而UICollectionView的FlowLayout默认会把每个section的内容作为独立的垂直块排列——哪怕单个item的宽度很小,每个section的item也会占据单独一行,最终呈现出单列的效果。
下面给你两种可行的解决方案:
方案一:将所有Channel放入同一个SectionController(推荐)
这种方式更符合IGListKit的设计逻辑,把同类型的多个item归到一个section中:
1. 创建一个Section模型类
用来包裹所有Channel,作为SectionController的数据源:
struct ChannelSectionModel: ListDiffable { let channels: [Channel] private let sectionId = "channel_group" func diffIdentifier() -> NSObjectProtocol { return sectionId as NSObjectProtocol } func isEqual(toDiffableObject object: ListDiffable?) -> Bool { guard let other = object as? ChannelSectionModel else { return false } // 这里需要Channel实现Equatable,或者对比所有channel的id return channels.map(\.id) == other.channels.map(\.id) } }
2. 修改ViewController的数据源逻辑
返回包含所有Channel的单个section模型:
class ViewController: UIViewController, ListAdapterDataSource { lazy var adapter: ListAdapter = { return ListAdapter(updater: ListAdapterUpdater(), viewController: self) }() @IBOutlet weak var collectionView: UICollectionView! // 用SectionModel包裹所有Channel let channelSection = ChannelSectionModel(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 // 配置FlowLayout的间距,确保双列布局美观 if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout { flowLayout.minimumInteritemSpacing = 8 flowLayout.minimumLineSpacing = 8 flowLayout.sectionInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) } } func objects(for listAdapter: ListAdapter) -> [ListDiffable] { return [channelSection] // 返回单个section模型 } func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController { return MultiChannelSectionController() } func emptyView(for listAdapter: ListAdapter) -> UIView? { return nil } }
3. 实现支持多item的SectionController
final class MultiChannelSectionController: ListSectionController { var sectionModel: ChannelSectionModel? override func numberOfItems() -> Int { return sectionModel?.channels.count ?? 0 } override func sizeForItem(at index: Int) -> CGSize { guard let containerWidth = collectionContext?.containerSize.width else { return .zero } // 计算双列宽度:减去左右内边距和item间距 let availableWidth = containerWidth - 8*2 - 8 // 左右各8内边距,中间8间距 let itemWidth = availableWidth / 2 return CGSize(width: itemWidth, height: itemWidth) } override func cellForItem(at index: Int) -> UICollectionViewCell { guard let sectionModel = sectionModel, index < sectionModel.channels.count else { fatalError() } let channel = sectionModel.channels[index] guard let cell = collectionContext?.dequeueReusableCellFromStoryboard(withIdentifier: "ChannelCell", for: self, at: index) as? ChannelCollectionViewCell else { fatalError() } cell.channel = channel return cell } override func didUpdate(to object: Any) { sectionModel = object as? ChannelSectionModel } }
4. 确保Channel实现完整的ListDiffable协议
你的原代码中强制转换channels as! [ListDiffable],需要Channel正确实现协议方法:
class Channel: ListDiffable { let id: String let color: UIColor init(id: String, color: UIColor) { self.id = id self.color = color } func diffIdentifier() -> NSObjectProtocol { return id as NSObjectProtocol } func isEqual(toDiffableObject object: ListDiffable?) -> Bool { guard let other = object as? Channel else { return false } return id == other.id && color == other.color } }
方案二:保持单个Channel对应SectionController,修改Layout配置(不推荐)
如果一定要保持每个Channel单独一个section,可以通过修改FlowLayout的sectionInsetReference和强制让section横向排列,但这种方式会破坏IGListKit的section语义,不建议使用:
override func viewDidLoad() { super.viewDidLoad() // ...其他代码 if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout { flowLayout.scrollDirection = .horizontal flowLayout.minimumLineSpacing = 8 flowLayout.sectionInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) // 同时修改sizeForItem的宽度为合适值 } }
关键注意点
- 确认你的CollectionView在Storyboard中使用的是
UICollectionViewFlowLayout(默认就是这个),如果是自定义Layout需要额外适配。 sizeForItem中计算宽度时,一定要考虑内边距和item间距,否则会出现换行异常。
内容的提问来源于stack exchange,提问作者ken
相关产品推荐
相关产品推荐

