如何在UICollectionView Flow Layout中实现带中间通道的巴士座位表
如何用UICollectionViewFlowLayout实现巴士座位中间的通道间距
嘿,作为iOS新手刚接触UICollectionView布局,遇到这种需求完全不用慌!我给你两种实用方案,从简单易上手到灵活自定义,你可以根据自己的项目情况选择:
方案一:用多Section实现通道(最适合新手)
这种方法不用写复杂的自定义布局,只需要把座位分成左排和右排两个Section,然后通过Section的内边距来留出中间的通道:
步骤1:划分座位数据
把你的座位数据源拆成两个数组,一个存左排座位,一个存右排座位,对应UICollectionView的两个Section。
步骤2:实现FlowLayout代理方法
在你的ViewController里(或者专门的代理类)实现UICollectionViewDelegateFlowLayout的以下方法,给两个Section设置左右内边距,凑出中间的通道宽度:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { let aisleHalfWidth: CGFloat = 20 // 通道总宽度是40,左右各留20 let sidePadding: CGFloat = 16 // 左右两侧的边距 if section == 0 { // 左排座位 return UIEdgeInsets(top: 16, left: sidePadding, bottom: 16, right: aisleHalfWidth) } else { // 右排座位 return UIEdgeInsets(top: 16, left: aisleHalfWidth, bottom: 16, right: sidePadding) } } // 别忘了设置每个座位的大小 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: 60, height: 60) // 根据你的需求调整 }
这样两个Section之间就会自动留出40的通道宽度,左排和右排的座位也会整齐排列,新手很容易上手!
方案二:自定义UICollectionViewFlowLayout(更灵活)
如果不想拆分Section,想在同一个Section里实现完整的座位布局(包括通道),可以自定义FlowLayout,通过重写布局方法来手动调整每个座位的位置:
步骤1:创建自定义FlowLayout类
新建一个继承自UICollectionViewFlowLayout的类,比如BusSeatLayout,然后重写两个关键方法:
class BusSeatLayout: UICollectionViewFlowLayout { // 自定义通道宽度,可根据需求调整 var aisleWidth: CGFloat = 40 // 每行的座位数(左右各2个,总共4个) private let itemsPerRow = 4 override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { guard let originalAttrs = super.layoutAttributesForElements(in: rect) else { return nil } // 复制原属性,避免修改系统默认数据 let attrs = originalAttrs.map { $0.copy() as! UICollectionViewLayoutAttributes } // 计算每个座位的宽度(减去边距、座位间距和通道宽度后平分) let totalAvailableWidth = collectionView!.bounds.width - sectionInset.left - sectionInset.right - minimumInteritemSpacing * CGFloat(itemsPerRow - 1) - aisleWidth let itemWidth = totalAvailableWidth / CGFloat(itemsPerRow) for attr in attrs { let itemIndex = attr.indexPath.item let row = itemIndex / itemsPerRow // 当前座位所在的行 let column = itemIndex % itemsPerRow // 当前座位所在的列 // 计算基础X坐标 var xPos = sectionInset.left + CGFloat(column) * (itemWidth + minimumInteritemSpacing) // 如果是右半部分的座位(第2、3列,从0开始计数),加上通道宽度 if column >= 2 { xPos += aisleWidth } // 计算Y坐标 let yPos = sectionInset.top + CGFloat(row) * (itemSize.height + minimumLineSpacing) // 更新座位的frame attr.frame = CGRect(x: xPos, y: yPos, width: itemWidth, height: itemSize.height) } return attrs } // 重写内容尺寸计算,确保滚动范围正确 override var collectionViewContentSize: CGSize { guard let collectionView = collectionView else { return .zero } let totalItems = collectionView.numberOfItems(inSection: 0) let totalRows = CGFloat((totalItems + itemsPerRow - 1) / itemsPerRow) // 向上取整计算总行数 let contentHeight = sectionInset.top + sectionInset.bottom + totalRows * (itemSize.height + minimumLineSpacing) - minimumLineSpacing // 减去最后一行的底部间距 return CGSize(width: collectionView.bounds.width, height: contentHeight) } }
步骤2:给CollectionView设置自定义Layout
在你的ViewController里,初始化这个自定义Layout并设置给CollectionView:
override func viewDidLoad() { super.viewDidLoad() let seatLayout = BusSeatLayout() seatLayout.itemSize = CGSize(width: 60, height: 60) seatLayout.minimumInteritemSpacing = 8 // 座位之间的横向间距 seatLayout.minimumLineSpacing = 12 // 行与行之间的纵向间距 seatLayout.sectionInset = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) seatLayout.aisleWidth = 40 // 设置通道宽度 collectionView.collectionViewLayout = seatLayout // 其他CollectionView设置(注册Cell、设置数据源等) }
小贴士
- 如果你的巴士座位是左右各3列,只需要把
itemsPerRow改成6,然后判断column >=3时加上通道宽度就行 - 测试时可以给每个Cell设置不同的背景色,方便观察布局是否正确
- 如果需要区分靠窗、过道座位,还可以在
layoutAttributesForElements里给不同列的座位设置不同的尺寸或样式
内容的提问来源于stack exchange,提问作者user1514567
相关产品推荐
相关产品推荐

