I am trying to develop an iOS application that has 03 UICollectionViews handled by the same ViewController. (The reason why I am opting for 03 UICollectionViews rather than one with sections and different prototype cells is because I may need to add additional content not relevant to collection views between the sections in the future)
________________
| _ _
| | | | |
| |_| |_| . . . (UICollectionView1)
|_______________
_______________
| _ _
| | | | |
| |_| |_| . . . (UICollectionView2)
|_______________
My problem is as follows:
No of cells in each of the CollectionViews is variable and if the number exceeds the width constraint it wraps down (so far so good). However, the height constraint of the UICollectionView causes a scroll bar to appear rather than simply laying out the cells if the number of cells causes to wrap beyond the height constraint
I have tried a couple of things to get this to work, most of which revolve around the advice given in the following questions
how to set dynamic height of a Collection View, the 'view' not the 'cells'?
How to adjust height of UICollectionView to be the height of the content size of the UICollectionView?
In the end I tried this
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: collectionView.contentSize.height)
}
But then the content in the UICollectionViewCell stretched wierdly and still problem of scrolling exists.
I do not mind that the main view of the view controller (the one on which all other UICollectionViews are placed) becomes scrollable (actually that is part of the requirement), I just don't want the UICollectionViews to act like some HTML iframe and allow scrolling but just layout the cells in order for as much as constrained by width of the UICollectionView
In pseudo code, something like this
array = (cell1, cell2, cell3)
for i in array
if currentCollectionViewRow is filled
wrapToNextLine()
add cell{i} to view controller
Any help is appreciated. Even help that suggests better ways to achieve this functionality along with best practices rather than hacking code
EDIT
I carried out the instructions as #Saad Chaudhry mentioned but to no avail. My layout is as follows: CollectionView Layout
As you can see, the stack view encloses both collection views as suggested. Now the IDE gives the following complaints: Ambiguous Layout
I tried adding constraints intuitively, then tried the IDEs options to add constraints automatically to no avail. Most times, there is no data cells on the screen.
For more information, without stack view but just two collection views, I get the following simulation: Without Stack Views
And with stack views I get the simulation: With stack views and note that the second collection view is missing
If I then constraint the stack view at 0,0,0,0, this brings back the issues where each of the collection view heights are ambiguous. Providing heights causes scrolling "within that collection view" if the number of cells are large (from datasource).
I simply want all the black squares to be rendered first and then the yellow squares. The parent view may scroll and that's fine bu not the individual collection views
My code for the controller:
import UIKit
class DemoCollectionViewController: UICollectionViewController {
#IBOutlet weak var nonPriorityCollectionView: UICollectionView!
#IBOutlet weak var priorityCollectionView: UICollectionView!
private let reuseIdentifier = "priorityCell"
fileprivate let sectionInsets = UIEdgeInsets(top: 50.0, left: 20.0, bottom: 50.0, right: 20.0)
fileprivate var priorityItems = [PriorityItem]()
fileprivate let itemsPerRow: CGFloat = 3
private let nonPriorityReuseIdentifier = "nonPriorityCell"
fileprivate var nonPriorityItems = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: nonPriorityReuseIdentifier)
loadPriorityItems()
loadNonPriorityItems()
}
func loadPriorityItems(){
let item1 = PriorityItem(image: #imageLiteral(resourceName: "User"))
let item2 = PriorityItem(image: #imageLiteral(resourceName: "User"))
let item3 = PriorityItem(image: #imageLiteral(resourceName: "User"))
let item4 = PriorityItem(image: #imageLiteral(resourceName: "User"))
priorityItems = [item1, item2, item3, item4, item1, item2, item3, item4]
}
func loadNonPriorityItems(){
let item1 = "Item 1"
let item2 = "Item 2"
let item3 = "Item 3"
let item4 = "Item 4"
nonPriorityItems = [item1, item2, item3, item4]
}
}
// MARK: UICollectionViewDataSource
extension DemoCollectionViewController {
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == self.priorityCollectionView {
return priorityItems.count
}
else{
return nonPriorityItems.count
}
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.priorityCollectionView {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
cell.backgroundColor = UIColor.black
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: nonPriorityReuseIdentifier, for: indexPath)
cell.backgroundColor = UIColor.yellow
return cell
}
}
}
extension DemoCollectionViewController : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
let availableWidth = view.frame.width - paddingSpace
let widthPerItem = availableWidth / itemsPerRow
return CGSize(width: widthPerItem, height: widthPerItem)
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAt section: Int) -> UIEdgeInsets {
return sectionInsets
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return sectionInsets.left
}
}
Please help me in figuring out this issue. I now feel like collection view is not the ideal way to achieve this due to the complexity I'm facing...
Related
I'm trying to have collection view with 2 column but dynamic height.
I have used Autolayout and given required constraints to the Cell
By this way I can calculate the dynamic height but its column grids fails.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! MenuListCollectionViewCell
cell.frame.size.width = collectionView.frame.width/2
cell.menuList = self.menuList[indexPath.row]
let resizing = cell.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize, withHorizontalFittingPriority: UILayoutPriority.required, verticalFittingPriority: UILayoutPriority.fittingSizeLevel)
return resizing
}
This is how I want it to look
Not sure what you mean by "its column grid fails".
Anyway, you need to write a custom collection view layout. The default one (UICollectionViewFlowLayout) allows you to change height of the cells by providing the sizeForItemAt, but that won't change the behavior of the layout that will always arrange cells in rows of the same height (the height of the highest cell).
If I understood correctly, you just want the same layout of this raywenderlich tutorial.
Basically:
Create a subclass of UICollectionViewLayout implementing it's methods:
collectionViewContentSize: return width and height of the collection view content
prepare: where you can calculate the sizes of cells and
collectionView content
layoutAttributesForElements: where you
return an array of UICollectionViewLayoutAttributes in the given
rect
layoutAttributesForItem: where you return the same kind
of attributes, this time specific for an item
assign an object of this class to the collection view layout property
you can use this
This code is somehow written that you can change section inset or minimumInteritemSpacing and this calculate and resize with this parameters
you can use this code or download project from Github
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let numberofItem: CGFloat = 2
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let collectionViewWidth = self.collectionView.bounds.width
let extraSpace = (numberofItem - 1) * flowLayout.minimumInteritemSpacing
let inset = flowLayout.sectionInset.right + flowLayout.sectionInset.left
let width = Int((collectionViewWidth - extraSpace - inset) / numberofItem)
return CGSize(width: width, height: width)
}
I was able to achieve your desired outcome by doing the following.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 20
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (self.view.frame.size.width/2 - 5), height: 100)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = customCollectionView.dequeueReusableCell(withReuseIdentifier: "test", for: indexPath)
cell.layer.backgroundColor = UIColor.red.cgColor
return cell
}
Make sure you have implemented all the correct delegates
UICollectionViewDelegate
UICollectionViewDatasource
UICollectionViewDelegateFlowLayout
where you see height: make it your own desired height as you have specified in your question.
KEY: Make sure in your storyboard you have set the estimate size of the collection view to NONE -> otherwise the code will not work as expected
I am using Swift to build an iOS application for the Hospital I work at.
Somehow, in a specific feature I have to put a UICollectionView inside the UICollectionViewCell. The one I want to achieve was for every content of the parent UICollectionView (vertical scrolling) would have several children (Which can be scrolled horizontal) depending on the parent row.
For illustration, I have to display list of doctors (name & photo) and then I have to display each of the practice schedule of them below their name and photo. The practice schedule would vary depending on each doctor. So, I have to put it inside the UICollectionView.
I have tried several solutions that I found on the web, but I still cannot approach it.
The most problem that I can't solve was: I don't know where is the place in the code to load the child data source (doctor schedule) and when I could load it, because I can't have two functions like below:
collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
this is the one I want to achieve
the UIImage and doctor name (UILabel) was in the parent UICollectionViewCell (scroll vertically), and then everything in the box (practice day n practice time) are the child UICollectionView (scroll horizontally)
PS: there are many doctors, and each of the doctor has several practice day.
please help me how to do this
If you really want to insert an collectionView inside a collectionViewCell then there is a pretty simple step. Create an instance of UICollectionView and add it the collectionViewCell. You can use this example if you like.
//
// ViewController.swift
// StackOverFlowAnswer
//
// Created by BIKRAM BHANDARI on 18/6/17.
// Copyright © 2017 BIKRAM BHANDARI. All rights reserved.
//
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
let cellId = "CellId"; //Unique cell id
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red; //just to test
collectionView.register(Cell.self, forCellWithReuseIdentifier: cellId) //register collection view cell class
setupViews(); //setup all views
}
func setupViews() {
view.addSubview(collectionView); // add collection view to view controller
collectionView.delegate = self; // set delegate
collectionView.dataSource = self; //set data source
collectionView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true; //set the location of collection view
collectionView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true; // top anchor of collection view
collectionView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true; // height
collectionView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true; // width
}
let collectionView: UICollectionView = { // collection view to be added to view controller
let cv = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()); //zero size with flow layout
cv.translatesAutoresizingMaskIntoConstraints = false; //set it to false so that we can suppy constraints
cv.backgroundColor = .yellow; // test
return cv;
}();
//deque cell
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath);
// cell.backgroundColor = .blue;
return cell;
}
// number of rows
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5;
}
//size of each CollecionViewCell
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: 200);
}
}
// first UICollectionViewCell
class Cell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let cellId = "CellId"; // same as above unique id
override init(frame: CGRect) {
super.init(frame: frame);
setupViews();
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId); //register custom UICollectionViewCell class.
// Here I am not using any custom class
}
func setupViews(){
addSubview(collectionView);
collectionView.delegate = self;
collectionView.dataSource = self;
collectionView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true;
collectionView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true;
collectionView.topAnchor.constraint(equalTo: topAnchor).isActive = true;
collectionView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true;
}
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout();
layout.scrollDirection = .horizontal; //set scroll direction to horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout);
cv.backgroundColor = .blue; //testing
cv.translatesAutoresizingMaskIntoConstraints = false;
return cv;
}();
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath);
cell.backgroundColor = .red;
return cell;
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5;
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.frame.width, height: self.frame.height - 10);
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
This might be a little late, but for people out here still trying to find an answer.
After some research and digging, I stumbled upon several posts stating reasons why you should NOT have your cell be the delegate for you collectionView. So, I was lost because pretty much all solutions I had found were doing this, until I finally found what I believe is the best way to have nested collectionViews.
To give some background, my app included not only one but 2 collectionViews inside different cells of another collectionView, so setting the delegates with tags and all that, wasn't really the best approach nor the correct OO solution.
So the best way to do it is the following:
First you have to created a different class to serve as your delegate for the inner collectionView. I did it as such:
class InnerCollectionViewDelegate: NSObject, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
// CollectionView and layout delegate methods here
// sizeForItemAt, cellForItemAt, etc...
}
Now, in your inner collectionView (or rather the cell where you have the inner collectionView) create a function that will allow you to set its delegates
class InnerCell: UICollectionViewCell {
var collectionView: UICollectionView
init() {
let layout = UICollectionViewFlowLayout()
collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: frame.width, height: frame.height), collectionViewLayout: layout)
}
func setCollectionViewDataSourceDelegate(dataSourceDelegate: UICollectionViewDataSource & UICollectionViewDelegate) {
collectionView.delegate = dataSourceDelegate
collectionView.dataSource = dataSourceDelegate
collectionView.reloadData()
}
}
And lastly, in your ViewController where you have your outermost (main) collectionView do the following:
First instantiate the delegate for the inner collectionView
var innerDelegate = InnerCollectionViewDelegate()
and then
override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if let cell = cell as? InnerCell {
cell.setCollectionViewDataSourceDelegate(dataSourceDelegate: innerDelegate)
}
}
This might not be perfect, but at least you have separation of concerns, as your cell should NOT be the delegate. Remember your cell should only be responsible for displaying info, not trying to figure out what the size of the collectionView should be, etc.
I did find similar answers that dealt with setting the collectionViews tag and whatnot, but I found that that made it way harder to deal with each collectionView individually, plus dealing with tags can't result in spaghetti code or unintended behaviours.
I left out registering and dequeuing the cell, but I'm sure you're all familiar with that. If not, just let me know and I'll try to walk you through it.
There are multiple ways to tackle the problem of a horizontal collection inside another a vertical list collection.
The simplest would be to make the ViewController you are presenting the main UICollectionView to the dataSouce and delegate for both collection views. You can set the collection view inside the cell also to be served from here.
This article about placing collection view inside a table view explains the problem in a much elaborate way and the code for the same can be found here.
Add collectionView in collection view cell , and add delagate methods in collectionviewclass.swift. Then pass list you want to show in cell in collectionview's cellforrowatindexpath. If you didn't success on implimenting it then let me know . i will provide you code as i have already implemented it in that way.
my qustion is simple.I have searched a lot and i'm familiar with UICollectionView but never saw this behavior of UICollectionView. please don't answer quickly and please read all my question.
I'm working on a project that uses collectionView (and auto layout). my collectionView is paging enabled and is working fine. but when I scroll it too fast (I mean very too fast) it scrolls two pages and shows the contents for next next row (previous previous row). also this code :
decelerationRate = UIScrollViewDecelerationRateFast
didn't work.
I created a temp project that have 1 controller and a simple collectionView (that is paging enabled and the cells's size are same as the viewController size). and there is just a label in the cell. the lable number is same as the collectionView row. also set this:
decelerationRate = UIScrollViewDecelerationRateFast
when I scroll it very fast from 1 to 2, it stops on 3 or vice versa.
this is my temp project code (that is auto layout):
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let collectionViewLayout = UICollectionViewFlowLayout()
collectionViewLayout.sectionInset = UIEdgeInsets.zero
collectionViewLayout.minimumLineSpacing = 0
collectionViewLayout.minimumInteritemSpacing = 0
collectionViewLayout.scrollDirection = UICollectionViewScrollDirection.horizontal
self.collView.collectionViewLayout = collectionViewLayout
self.collView.decelerationRate = UIScrollViewDecelerationRateFast
}
func numberOfSections(in collectionView: UICollectionView) -> Int
{
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollCell
print("cell For Item At Row \(indexPath.row)")
cell.lblNumber.text = "\(indexPath.row)"
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: collectionView.frame.size.width, height: collectionView.frame.size.height)
}
anybody knows what should I do? is it a normal behavior for UICollectionView?
I am an android developer and developing my first iOS app, I need to implement GridView in order to add some social icon so after some search I found UICollectionView but it's not working as expected. How to set dynamic height of the UICollection view? Actually I want to display all icons but it showing only a row and others after scrolling the UICollectionView.
below is the example:
This is what I am getting
This is what I want:
I am using below code:
import UIKit
class CollectionViewDemo: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var socialHandleCollection: UICollectionView!
#IBOutlet weak var socialImageView: UIImageView!
var socialHandleArray:[String] = ["Facebook", "Twitter", "Youtube","Vimeo", "Instagram", "Custom URL", "Linkedin", "pinterest"]
override func viewDidLoad() {
self.socialHandleCollection.delegate = self
self.socialHandleCollection.dataSource = self
// socialHandleCollection.frame.size.height = 130
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return socialHandleArray.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: colvwCell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! colvwCell
cell.imgCell.image = UIImage(named: "demo_img.png")
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print(self.socialHandleArray[indexPath.row])
}
}
Any help would be appreciated.
Assuming you are using autoLayout
Initially declare a specific number of columns that you desire
let numberOfColumns = 5
Okay so first things first. You will have to make your class conform to the UICollectionViewDelegateFlowLayout. Now implement the function -
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize{
return CGSize(width: collectionView.bounds.size.width/numberOfColumns, height: collectionView.bounds.size.width/numberOfColumns);
}
Here 5 can be changed to the number of columns you want in each row and passing same value as height will ensure that the shape is always square. And hence you can apply corner radius to make it circular.
Now moving on. From your interface builder, ctrl + click and drag the UICollectionView height constraint to your UIViewController (similar to how you would do for a UIView but do it for the constraint)
Now once you know the number of items that you need to display inside your UICollectionView, you can do something like:
//replace 5 with the number of columns you want
//array contains the items you wish to display
func figureOutHeight(){
if(array.count == 0){
//as no items to display in collection view
return
}
//ceil function is just to round off the value to the next one, for example 6/5 will return 1 but we need 2 in this case. Ensure all arguments inside ceil function are float
let rowsCount = ceil(Float(array.count)/Float(numberOfColumns))
//now you have number of rows soo just update your height constraint by multiplying row count with height of each item inside UICollectionView
collectionViewHeightConstraint.constant = rowsCount * collectionView.bounds.size.height / numberOfColumns;
view.layoutIfNeeded()
}
Also if you haven't change the scroll direction to vertical.
How can I make a horizontal scrolling collectionView that fills up cells going across the rows rather than down the columns?
I want there to 5 columns and 3 rows but when there is more than 15 items I want it to scroll to the next page. I'm having a lot of trouble getting this going.
Where you have a reference to your UICollectionViewFlowLayout(), just do:
layout.scrollDirection = .horizontal
Here is a nice tutorial for more info: https://www.youtube.com/watch?v=Ko9oNhlTwH0
Though for historical purposes, consider searching StackOverFlow quickly to make sure this isn't a duplicate.
Hope this helps.
Update:
Your items will fill horizontally first and if there is not enough room within the collectionview going to the right, they will go to next row. So, start by increasing your collectionview.contentsize (should be larger the screen to enable scrolling) and then set your collectionview item (cell) size.
flowLayout.itemSize = CGSize(width: collectionView.contentSize.width/5, height: collectionView.contentSize.height/3)
Option 1 - Recommended
Use custom layouts for your collection view. This is the right way to do this and it gives you a lot of control over how you want your cells to fill the collection view.
Here is a UICollectionView Custom Layout Tutorial from "raywenderlich"
Option 2
This is more like a hackish way of doing what you want. In this method you can access your data source in an order to simulate the style you need. I'll explain it in the code:
var myArray = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]
let rows = 3
let columnsInFirstPage = 5
// calculate number of columns needed to display all items
var columns: Int { return myArray.count<=columnsInFirstPage ? myArray.count : myArray.count > rows*columnsInFirstPage ? (myArray.count-1)/rows + 1 : columnsInFirstPage }
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return columns*rows
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)
//These three lines will convert the index to a new index that will simulate the collection view as if it was being filled horizontally
let i = indexPath.item / rows
let j = indexPath.item % rows
let item = j*columns+i
guard item < myArray.count else {
//If item is not in myArray range then return an empty hidden cell in order to continue the layout
cell.hidden = true
return cell
}
cell.hidden = false
//Rest of your cell setup, Now to access your data You need to use the new "item" instead of "indexPath.item"
//like: cell.myLabel.text = "\(myArray[item])"
return cell
}
Here is this code in action:
*The "Add" button just adds another number to myArray and reloads the collection view to demonstrate how it would look with different number of items in myArray
Edit - Group items into pages:
var myArray = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]
let rows = 3
let columnsInPage = 5
var itemsInPage: Int { return columnsInPage*rows }
var columns: Int { return myArray.count%itemsInPage <= columnsInPage ? ((myArray.count/itemsInPage)*columnsInPage) + (myArray.count%itemsInPage) : ((myArray.count/itemsInPage)+1)*columnsInPage }
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return columns*rows
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)
let t = indexPath.item / itemsInPage
let i = indexPath.item / rows - t*columnsInPage
let j = indexPath.item % rows
let item = (j*columnsInPage+i) + t*itemsInPage
guard item < myArray.count else {
cell.hidden = true
return cell
}
cell.hidden = false
return cell
}
Specify the height of the collection view and cell size. More details below:
Set the constraints of the UICollectionView, pinning the edges. Be sure to specify the UICollectionView's height or constraints so it's clear the cells can only scroll horizontally and not go down to the next line. The height should be the same or slightly larger than the cell height you specify in step 2.
Implement the UICollectionViewDelegateFlowLayout delegate and sizeForItemAt method. Here's a sample sizeForItemAt implementation.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cellWidth = 100
let cellHeight = 30
return CGSize(width: cellWidth, height: cellHeight)
}