Swift 4
Code:
public class HorizontalFlowLayout: UICollectionViewLayout {
var itemSize = CGSize(width: 0, height: 0) {
didSet {
invalidateLayout()
}
}
private var cellCount = 0
private var boundsSize = CGSize(width: 0, height: 0)
public override func prepare() {
cellCount = self.collectionView!.numberOfItems(inSection: 0)
boundsSize = self.collectionView!.bounds.size
}
public override var collectionViewContentSize: CGSize {
let verticalItemsCount = Int(floor(boundsSize.height / itemSize.height))
let horizontalItemsCount = Int(floor(boundsSize.width / itemSize.width))
let itemsPerPage = verticalItemsCount * horizontalItemsCount
let numberOfItems = cellCount
let numberOfPages = Int(ceil(Double(numberOfItems) / Double(itemsPerPage)))
var size = boundsSize
size.width = CGFloat(numberOfPages) * boundsSize.width
return size
}
public override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var allAttributes = [UICollectionViewLayoutAttributes]()
for i in 0...(cellCount-1) {
let indexPath = IndexPath(row: i, section: 0)
let attr = self.computeLayoutAttributesForCellAt(indexPath: indexPath)
allAttributes.append(attr)
}
return allAttributes
}
public override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return computeLayoutAttributesForCellAt(indexPath: indexPath)
}
public override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
private func computeLayoutAttributesForCellAt(indexPath:IndexPath)
-> UICollectionViewLayoutAttributes {
let row = indexPath.row
let bounds = self.collectionView!.bounds
let verticalItemsCount = Int(floor(boundsSize.height / itemSize.height))
let horizontalItemsCount = Int(floor(boundsSize.width / itemSize.width))
let itemsPerPage = verticalItemsCount * horizontalItemsCount
let columnPosition = row % horizontalItemsCount
let rowPosition = (row/horizontalItemsCount)%verticalItemsCount
let itemPage = Int(floor(Double(row)/Double(itemsPerPage)))
let attr = UICollectionViewLayoutAttributes(forCellWith: indexPath)
var frame = CGRect(x: 0, y: 0, width: 0, height: 0)
frame.origin.x = CGFloat(itemPage) * bounds.size.width + CGFloat(columnPosition) * itemSize.width
frame.origin.y = CGFloat(rowPosition) * itemSize.height
frame.size = itemSize
attr.frame = frame
return attr
}
}
This is the Swift 3 version of @GuilhermeSprint answer
Code:
public class HorizontalCollectionViewLayout : UICollectionViewLayout {
var itemSize = CGSize(width: 0, height: 0) {
didSet {
invalidateLayout()
}
}
private var cellCount = 0
private var boundsSize = CGSize(width: 0, height: 0)
public override func prepare() {
cellCount = self.collectionView!.numberOfItems(inSection: 0)
boundsSize = self.collectionView!.bounds.size
}
public override var collectionViewContentSize: CGSize {
let verticalItemsCount = Int(floor(boundsSize.height / itemSize.height))
let horizontalItemsCount = Int(floor(boundsSize.width / itemSize.width))
let itemsPerPage = verticalItemsCount * horizontalItemsCount
let numberOfItems = cellCount
let numberOfPages = Int(ceil(Double(numberOfItems) / Double(itemsPerPage)))
var size = boundsSize
size.width = CGFloat(numberOfPages) * boundsSize.width
return size
}
public override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var allAttributes = [UICollectionViewLayoutAttributes]()
for i in 0...(cellCount-1) {
let indexPath = IndexPath(row: i, section: 0)
let attr = self.computeLayoutAttributesForCellAt(indexPath: indexPath)
allAttributes.append(attr)
}
return allAttributes
}
public override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return computeLayoutAttributesForCellAt(indexPath: indexPath)
}
public override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
private func computeLayoutAttributesForCellAt(indexPath:IndexPath)
-> UICollectionViewLayoutAttributes {
let row = indexPath.row
let bounds = self.collectionView!.bounds
let verticalItemsCount = Int(floor(boundsSize.height / itemSize.height))
let horizontalItemsCount = Int(floor(boundsSize.width / itemSize.width))
let itemsPerPage = verticalItemsCount * horizontalItemsCount
let columnPosition = row % horizontalItemsCount
let rowPosition = (row/horizontalItemsCount)%verticalItemsCount
let itemPage = Int(floor(Double(row)/Double(itemsPerPage)))
let attr = UICollectionViewLayoutAttributes(forCellWith: indexPath)
var frame = CGRectMake(0, 0, 0, 0)
frame.origin.x = CGFloat(itemPage) * bounds.size.width + CGFloat(columnPosition) * itemSize.width
frame.origin.y = CGFloat(rowPosition) * itemSize.height
frame.size = itemSize
attr.frame = frame
return attr
}
}
Usage:
// I want to have 4 items in the page / see screenshot below
let itemWidth = collectionView.frame.width / 2.0
let itemHeight = collectionView.frame.height / 2.0
let horizontalCV = HorizontalCollectionViewLayout();
horizontalCV.itemSize = CGSize(width: itemWidth, height: itemHeight)
collectionView.collectionViewLayout = horizontalCV
Result
My Delegates extension if you wanna check it alsoextension MyViewController : UICollectionViewDelegateFlowLayout, UICollectionViewDataSource{
// Delegate
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Clicked")
}
// DataSource
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: BottomMenuCCell.xib, for: indexPath) as? BottomMenuCCell {
cell.ibi = bottomMenuButtons[indexPath.row]
cell.layer.borderWidth = 0
return cell
}
return BaseCollectionCell()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize.init(width: (collectionView.width / 2.0), height: collectionView.height / 2.0)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return bottomMenuButtons.count
}
// removing spacing between items
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0.0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0.0
}
}