UICollectionView layout behave weirdly on scrolling - ios

I'm setting a collection View in order to display content as on instagram. instagram's grid
In order to set the layout I'm using some delegate methods of UIcollectionviewDelegateFlowLayout :
extension ProfileViewController : UICollectionViewDelegateFlowLayout{
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
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (view.frame.width)/3, height: (view.frame.width)/3)
}
}
When I open the project, the collectionView works well.
The problem occurs when you scroll down very fast the collectionView so that you make momentaneously disappear all the cells.
In that exact moment the cell layout I've set before doesn't hold and the images become ultra zoomed and one on top of each other, like this: messed Up collectionView
I don't know how to fix this issue.
Also in the compiler I get this error :
the item width must be less than the width of the UICollectionView minus the section insets left and right values, minus the content insets left and right values.
Please check the values returned by the delegate.
The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x7fbbe972d1f0>, and it is attached to <UICollectionView: 0x7fbbeb056400; frame = (0 0; 414 896); clipsToBounds = YES; autoresize = RM+BM;
gestureRecognizers = <NSArray: 0x60000203c4e0>; layer = <CALayer: 0x600002ef19c0>; contentOffset: {0, 424}; contentSize: {414, 16048}; adjustedContentInset: {88, 0, 83, 0}; layout: <UICollectionViewFlowLayout: 0x7fbbe972d1f0>; dataSource: <InstagramClone.ProfileViewController: 0x7fbbe9708890>>.

I think your problem could be the same as the problem in this post.
Try fixing your problem by setting the UICollectionView's estimatedSize to none.
This is because in Xcode 11 Cells in a CollectionView can now self-size with Auto Layout constrained views in the canvas.

Related

UICollectionView flow layout not defined

I have a horizontally scrolling UICollectionView using the flow layout that has its delegate and datasource set. It has a height of 50 and is pinned to my view using autolayout. Unfortunately, it never displays and always gives the following error:
2020-10-09 21:30:08.687110+0100 MyApp[8679:1601772] The behavior of the UICollectionViewFlowLayout is not defined because:
2020-10-09 21:30:08.687428+0100 MyApp[8679:1601772] the item height must be less than the height of the UICollectionView minus the section insets top and bottom values, minus the content insets top and bottom values.
2020-10-09 21:30:08.687743+0100 MyApp[8679:1601772] Please check the values returned by the delegate.
2020-10-09 21:30:08.688641+0100 MyApp[8679:1601772] The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x105dc6350>, and it is attached to <UICollectionView: 0x10611d000; frame = (0 0; 375 90); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x28372ccf0>; layer = <CALayer: 0x2839465c0>; contentOffset: {0, 0}; contentSize: {1081.6666666666667, 90}; adjustedContentInset: {0, 0, 0, 0}> collection view layout: <UICollectionViewFlowLayout: 0x105dc6350>.
2020-10-09 21:30:08.688943+0100 MyApp[8679:1601772] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.
I've noticed that it is saying the contentSize is {1081.6666666666667, 90} but it actually only calls the numberOfSections and numberOfItemsInSection delegate methods. The others aren't called, and neither is the UICollectionViewDelegateFlowLayout sizing method. Why is this and how is it getting a contentSize without actually calling the datasource/delegate methods? It makes no difference if I set the autolayout height from 50 to 90.
My collectionView is set up in the storyboard. I have registered my cell and set the estimated flow layout size like so:
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(
UINib(nibName: "VideoClipCell", bundle: nil),
forCellWithReuseIdentifier: "VideoClipCell"
)
if let flowLayout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.estimatedItemSize = CGSize(width: 30, height: 50)
}
}
These are the datasource/flow delegate methods:
extension EditingViewController : UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: "VideoClipCell", for: indexPath) as? VideoClipCell else {
return UICollectionViewCell()
}
return cell
}
}
extension EditingViewController: UICollectionViewDelegateFlowLayout {
func collectionView(
_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height: 50)
}
}
Do you have an UIImageView in your cell? That's probably why you're getting 1081.6666666666667 for the content size.
You probably don't want self-sizing cells, so turn off "Estimate Size" in the storyboard. Otherwise, your collection view won't read what's inside sizeForItemAt.
Can you show your UICollectionView`s init func,i guess you did not set the UICollectionView delegate to self,and the layout itemsize height is larger than 90

CollectionView Constraints breaking

I have a collectionView and each cell contains a tableView inside it. The gray portion is supposed to be the collectionView.
But when I run the app, I get something like this
As you can clearly see that the table view present doesn't stick to the constraints set. I get the following errors from the console.
The behavior of the UICollectionViewFlowLayout is not defined because:
the item height must be less than the height of the UICollectionView minus the section insets top and
bottom values, minus the content insets top and bottom values.
The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x7f80fd28e420>, and it
is attached to <UICollectionView: 0x7f80fc08f800; frame = (0 114; 375 664); clipsToBounds = YES;
autoresize = RM+BM; gestureRecognizers = <NSArray: 0x600000d6c4b0>; layer = <CALayer: 0x6000003beee0>;
contentOffset: {0, 0}; contentSize: {0, 0}; adjustedContentInset: {0, 0, 0, 0};
layout: <UICollectionViewFlowLayout: 0x7f80fd28e420>; dataSource: <BizTiz.EventDetailController: 0x7f80fa0b7800>>.
Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.
How do I fix this? Also when I push this collectionView onto the stack, there is a small lag. I am guessing this might be due to the previous error.
Please set layout in the UICollectionViewDelegateFlowLayout.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: <cell_width>, height: <cell_height>)
}
Realized my mistake....
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height - 114)
}
I was setting the height as view.frame.height. I had forgotten to subtract the extra height at top.

CollectionViewCell's container view doesn't fill cell

I have a collectionView who's height and width I set with constraints using autolayout.
In my
func collectionView(_ collectionView: ...., sizeForItemAt indexPath: IndexPath) I return CGSize(width: self.collectionView.frame.width-20, height: self.collectionView.frame.height) and as seen on the image linked below it lines up perfectly fine.
The problem is that the container view (white space) of that cell doesn't fill up the green space (that is the cell's background) at all even though I have set constraints of the containerView to hug left/right/bottom/up with 0 margin.
What I have tried so far:
Make a height constraint for the container view that I assign the collection view's height
updateConstraints() in cellForItemAt and inside the cell's class in ```layoutSubview()````
Set the container view's frame to the collectionView's frame
cell.layoutIfNeeded()in cellForItemAt
None of these have solved my problem.
https://i.stack.imgur.com/mD2iC.jpg
Here issue related to inset I think. Please use collection delegate method
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
return UIEdgeInsetsMake(top, left, bottom, right);
}
or
UICollectionViewFlowLayout *aFlowLayout = [[UICollectionViewFlowLayout alloc] init];
[aFlowLayout setSectionInset:UIEdgeInsetsMake(top, left, bottom, right)];
Swift 5.1
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 15.0
}

How can I set custom horizontal scroll for Collection View in Swift, XCode?

I want to set custom horizontal scroll for my CollectionView, that it will be scrolling by 1 cell (not by the whole width of my screen).
I could set HORIZONTAL scroll, but not custom. (See screens).
1 screen: my extension of my collectionView for UIScrollViewDelegate.
*I saw, that in console (see too) "x" - my offset = 290 - it's true! But on fact it is not 290. Paging was marked in "true" 2 screen: delegate and dataSource.
Help, please!
First you need to set your collectionView scroll direction horizontal (UICollectionViewScrollDirectionHorizontal) and set pagingEnabled to YES
It means your collection view will be scroll horizontally with one by one cell.
Set horizontal direction (Select collection view from XIB or Storyboard)
And for set pagination enable
myCollectionView.pagingEnabled = true
Updated :
You should use of UICollectionViewDelegateFlowLayout delegate
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
let width = UIScreen.main.bounds.size.width
var height = 101.7 // set height as you want
return CGSize(width: width, height: height)
}
If using storyBoard the go to collection view and select the right handed tab "show the size inspector" and change
minimum spacing for cells = 0
minimum spacing for lines = 0
if you want to change directly from code then implement these UICollectionViewDelegateFlowLayout methods
func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
must be remember scrolling direction set horizontal
and pagination enabled
and implement this delegate methods into code
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath:
IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.size.width, height:
collectionView.frame.size.height)
}
}
You could create a custom UICollectionViewFlowLayout that will center the cells in the screen and then page one at a time. Karmadust have written a good blog post that I have successfully used in the past to do the same thing
http://blog.karmadust.com/centered-paging-with-preview-cells-on-uicollectionview/

iOS UICollectionView with self sizing items bug?

I'm trying to implement UICollectionView (flowLayout) with self sizing items.
Implementation is very simple - I have just set estimatedItemSize and set UICollectionViewCell constraints to manage it's size.
Everything works fine at first data reload after collectionView was created, but on another or some other reload few items at the top becomes same size as estimatedItemSize is. If scroll down and up - items size looks good again.
I have spend 2 days with this issue experimenting different cell constraints, trying to setNeedsLayout in various places and other stuff around collectionView. Is it bug?
In this post I had an interesting problem that I found my own answer to. There are two important functions to be using properly for the sizing purposes which are:
minimumInteritemSpacingForSectionAtIndex
insetForSectionAtIndex
sizeForItemAtIndexPath
Example of me using them in Swift
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionView, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(0, 0, 0, 0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
let screenRect: CGRect = collectionView.bounds
let screenWidth: CGFloat = screenRect.size.width
let cellWidth: CGFloat = screenWidth / 24
//Replace the divisor with the column count requirement. Make sure to have it in float.
let size: CGSize = CGSize(width: cellWidth, height: cellWidth)
return size
}
The biggest secret is to be careful about attaching your views to the trailing edge and the bottom layout. If you only attach them to leading and the top, then you can set the width and height programmatically through frame or constraints. I think that doing it through constraints is a little more straight forward, though in my personal project I have chose to do it the other way because it makes the functionality slightly cleaner in my opinion.
Try to accomplish as much of a cells layout as possible inside of cellForItemAt.

Resources