In an iOS app I have a UICollectionView which has 100 (or 80, or 40) cells to start with.
At the beginning all is fine, the layout is OK and the size of each cell is also right, smaller if there is 100 and larger if there is only 40 cells.
Inside the app the user has the possibility to change the number of cells to 100 (or 80, or 40).
When the number changes the layout and size of each cell should be changed accordingly. But that part does not work as expected.
When the user changes the number of cells this code is executed:
myCollectionView.reloadData()
myCollectionView.collectionViewLayout.invalidateLayout()
And for some reasons something is not working. Should I do something more or different in the code above?
The number of cells is right, but the size of the news cells is messed up and I am not totally sure about the layout (positionning).
As far as the UICollectionViewDelegate protocol is concerned, I only implement one method:
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
.........
}
For the UICollectionViewDelegateFlowLayout protocol, I implement the following:
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let cellSide = UIScreen.main.bounds.width * 3 / CGFloat(brain.getSide()! * 4)
return CGSize(width: cellSide, height: cellSide)
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 6.0
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return UIScreen.main.bounds.width / (CGFloat(brain.getSide()!) * 4.4)
}
It seems like the methods above are working fine at start, giving adequate values. But they do not even seem to be called when the user changes the settings.
Please try this reloading method
Objective-c
[self.collectionView performBatchUpdates:^{
[myCollectionView reloadData];
}
completion:^(BOOL finished) {}
];
Swift-3
myCollectionView?.performBatchUpdates({
myCollectionView.reloadData()
}, completion: { (result: Bool) in
})
I ended up by solving the problem, using cell prototypes with a different cell identifier for each size I need and it works perfectly.
Related
I have a collection view with direction set to horizontal and paging enable. The things is, the height of my collection cell each page sometimes is higher than the phone screen size. What I want to achieve is user can scroll down the cell and swipe right to the next page.
Currently, I only able to display the collection cell one full page, without manage to scroll downward based on content height. Here some code:
extension EventListDetailsVC: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cat = dataArray[indexPath.row]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "celldetails", for: indexPath) as? EventListDetailsCVC
cell?.imageEvent.image = UIImage(named: cat.imageEvent)
cell?.eventTitleLabel.text = cat.titleEvent
cell?.eventDescriptionLabel.text = cat.content
return cell!
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.collectionView.frame.size.width, height: self.collectionView.frame.size.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets.zero
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
this how it should be
I tried to embed my collectionView into tableView, but it seems does not work because I have dynamic height in a cell.
Is it another way to achive this?
p/s I just started learning swift, thanks.
Have you tried embedding a scroll view inside each collection view cell?
That would make sure that the collection view cell height never exceeds the phone height while the scroll view will work on displaying more content as you scroll down whether it be text or images or whatever view you decide to embed in the scroll view itself.
I think you should try UIPageViewController with the scroll transition style. It has native pagination between pages.
Every page should have UIScrollView.
Width/height of my UICollectionView matches all available space.
I want to display two cells in one row (two columns)
So width of one cell should take half width of UICollectionView (collectionView.frame.width / 2)
So I programmatically change width of cell in function of UICollectionViewDelegateFlowLayout:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width / 2.0, height: 150)
}
But visually width of cell is much bigger than collectionView.frame.width / 2.0 (tested on iPhone SE simulator)
So it takes more than half-width space of screen
Here I printed info about sizes:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath)
print("didSelectItemAt \(cell.frame.width), table width: \(collectionView.frame.width), item calc width: \(collectionView.frame.width / 2)")
}
didSelectItemAt cell width 187.5, table width: 375.0, half-width:
187.5
Why does it happen?
There is also an issue with margins but first I have to solve this one
Update
Though it works fine with iPhone 6s Simulator (I edited image to place two items in the first row to check if they fit):
So what is wrong with iPhone SE?
I don't want to find out that there can be the same issue with other iPhones or iPads too
375.0 is the size of iPhone 6s and X, not iPhone SE which is 320.0
The reason is there is a width constraint of collectionView which was set at iPhone 6s mode but later when switching to the SE simulator, this constraint was not updated.
I think there is problem of cell spacing. it seems there is default cell spacing exists in your implementation. you should try below code to adjust two cell in one raw of collectionview:
extension ViewController : UICollectionViewDelegateFlowLayout{
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, sizeForItemAt indexPath: IndexPath) -> CGSize {
let collectionViewWidth = collectionView.bounds.width
return CGSize(width: collectionViewWidth/2, height: 150)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
moreover, you should check constraint of your UICollectionView if it is as per your requirement Or not.
Try to use the wide from the view, not the collectionView.
Try this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (view.frame.width / 2.0) - 5, height: 150) // provide little bit space between cells
}
Hope this will help.
The best solution is, to reload your collection view in main thread with a bit delay.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.10) {
self.myCollectionView.reloadData()
}
I saw this question asked on one other post, however, the answer was unsatisfactory.
I have a view controller with several collection views in it for which I size their cells perfectly with the storyboard. However, there is one collection view in particular that I need to size manually so I included the method:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
What I am trying to accomplish is to override or ignore this methods for all collection views but that last one.
I tried using the following code suggested on the other post, but it crashes at runtime:
if collectionView != interestsCollectionView {
return self.collectionView(collectionView, layout: UICollectionViewLayout(), sizeForItemAt: IndexPath(item: 0, section: 0))
}
Any ideas of how I might accomplish this?
I guess this
return self.collectionView(collectionView, layout: UICollectionViewLayout(), sizeForItemAt: IndexPath(item: 0, section: 0))
will recursively call the same method many times resulting in a crash caused by the loop , plus if you implemented this method sizeForItemAt then you'll have to return a value for the other collections also as the if here if collectionView != interestsCollectionView may never hit from the compiler logic , so you may do
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
if collectionView == collectionView1 {
return CGSize(width:<#w#>,height:<#h#>)
}
else if collectionView == collectionView2 {
return CGSize(width:<#w#>,height:<#h#>)
}
else { } // etc
}
I am trying to set my collection view cell sizes so that I get two cells per row.
I have looked at plenty of solution for sizing cells for collection views. I actually have used some solutions before for sizing three cells per row that have worked for me.
What I have so far:
extension BusinessViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = (view.frame.width - 20) / 2.0
return CGSize(width: width, height: width)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
}
Right now I just get one big cell per row and I don't know why. Usually when I have to have 3 cells per row, simply subtracting the insets from the views width and dividing by the number of desired cells is enough. Not sure why this is causing me an issue.
You can try checking the "estimatesSize" property of UICollectionView.
It could have been set to "Automatic" instead of "None".
I solved the same problem with this solution
I have a UICollectionView in my app. I want it to constrain so it only has two cells on each horizontal row. For iPhone classes I achieve this by setting the width so three cells can't fit in the same horizontal row. This seems to be a fix but I don't know how to resize the cell for iPads. I have heard to use:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
but I can't access this function. Does Swift no longer support this function anymore?
Question: Can I force my screen to only display 2 cells on each horizontal row?
You should first conform to protocol UICollectionViewDelegateFlowLayout
and then implement the method :
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.size.width * 0.5, height: 30)
}