CollectionViewCells are being mixed with each other - ios

Hello fellow overflowers so I set up a collectionView and configure it but when I run the simulator I get a lot of cells just mixed with each other like in the first picture.
I have added the identifier at the storyboards cell also created the class for the cell but I can't seem to know what the problem might be since it never happened.
How I want my cell to be I will show on the second picture.
first picture
second picture
the code:
extension dashboardViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{
return 15
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "dashboardCollectionViewCell", for: indexPath) as? dashboardCollectionViewCell else {return UICollectionViewCell()}
return cell
}
}

By the image provided, it seems you haven't set the item size of the cell
in horizontal layout as well as the minimum spacing between each cell. You can do that (set item size) by calling
func collectionView(
_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath
) -> CGSize {}
this method from UICollectionViewDelegateFlowLayout.
You can also do this using storyboard.
Size Inspector

Seems that your collection view cell's size has not correct set.
If your are using UICollectionViewFlowLayout, maybe you can check the layout object's itemSize property.
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
...
// Set a suitable CGSize for the `itemSize` property
// In this example I set it to 100pt * 50pt
layout.itemSize = CGSizeMake(100, 50);
...
UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];

Related

UICollectionViewCell With 2 column grid and dynamic height

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

When I scroll UICollectionView (paging enabled) too fast, it scrolls two pages

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?

Autoresize UICollectionViewCell size inside UITableView

I am trying to create simple educational app about movies, and I need to create movie frames horizontal scroller.
I create collection view for that:
But if I build it for another target (iPhone 6s Plus for example), I have this:
How to solve this problem?
My scene controller code (Swift 3):
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
cell.backgroundColor = UIColor.black
// Configure the cell
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
I think that you can try to implement the method of UICollectionView delegate:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(yourHeight, self.view.frame.size.width)
}
in this way, your collectionView-cell inside in tableView will appear as big as the screen.
First I want to say something about UITableViewAutomaticDimension you use, in case you not exactly understand how it works.
Your expectations probably are, that UITableViewAutomaticDimension will infer tableView to make a cell with size of entire tableView, but it doesn't work this way.
UITableViewAutomaticDimension is just infers tableView, that inner size of your cell will be used as a returned here; so if you add some constraints, views with constraints, or whatever inside your tV, its size will be used.
and second - your comment doesn't make sense at all for me, because your posted code isn't related to cell of collectionView and I can't comment anything related to size of its cells
EDITED:
to make all cells with width of your screen, return correct size from delegate method of collectionViewFlowLayout - sizeForItem

Variable width & height of UICollectionViewCell using AutoLayout with Storyboard (without XIB)

Here is an design issue in the app that uses AutoLayout, UICollectionView and UICollectionViewCell that has automatically resizable width & height depending on AutoLayout constraints and its content (some text).
It is a UITableView list like, with each cell that has it's own width & height calculated separately for each row dependant on its content. It is more like iOS Messages build in app (or WhatsUp).
It is obvious that app should make use of func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize.
Issue is that within that method, app cannot call func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell nor dequeueReusableCellWithReuseIdentifier(identifier: String, forIndexPath indexPath: NSIndexPath!) -> AnyObject to instantiate cell, populate it with specific content and calculate its width & height. Trying to do that will result in an indefinite recursion calls or some other type of app crash (at least in iOS 8.3).
The closest way to fix this situation seems to copy definition of the cell into view hierarchy to let Auto-layout resize "cell" automatically (like cell to have the same width as parent collection view), so app can configure cell with specific content and calculate its size. This should definitely not be the only way to fix it because of duplicated resources.
All of that is connected with setting UILabel.preferredMaxLayoutWidth to some value that should be Auto-Layout controllable (not hardcoded) that could depend on screen width & height or at least setup by Auto-layout constraint definition, so app can get multiline UILabel intrinsic size calculated.
I would not like to instantiate cells from XIB file since Storyboards should be today's industry standard and I would like to have as less intervention in code.
EDIT:
The basic code that cannot be run is listed bellow. So only instantiating variable cellProbe (not used) crashes the app. Without that call, app runs smoothly.
var onceToken: dispatch_once_t = 0
class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
dispatch_once(&onceToken) {
let cellProbe = collectionView.dequeueReusableCellWithReuseIdentifier("first", forIndexPath: NSIndexPath(forRow: 0, inSection: 0)) as! UICollectionViewCell
}
return CGSize(width: 200,height: 50)
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("first", forIndexPath: NSIndexPath(forRow: 0, inSection: 0)) as! UICollectionViewCell
return cell
}
}
If you are using constraints, you don't need to set a size per cell. So you should remove your delegate method func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
Instead you need to set a size in estimatedItemSize by setting this to a value other than CGSizeZero you are telling the layout that you don't know the exact size yet. The layout will then ask each cell for it's size and it should be calculated when it's required.
let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.estimatedItemSize = someReasonableEstimatedSize()
Just like when dynamically size Table View Cell height using Auto Layout you don't need call
func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
in
optional func tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
the proper way is create a local static cell for height calculation like a class method of the custom cell
+ (CGFloat)cellHeightForContent:(id)content tableView:(UITableView *)tableView {
static CustomCell *cell;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cell = [tableView dequeueReusableCellWithIdentifier:[CustomCell cellIdentifier]];
});
Item *item = (Item *)content;
configureBasicCell(cell, item);
[cell setNeedsLayout];
[cell layoutIfNeeded];
CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return size.height + 1.0f; // Add 1.0f for the cell separator height
}
and I seriously doubt storyboard is some industry standard. xib is the best way to create custom cell like view including tableViewCell and CollectionViewCell
To calculate the CollectionView cell's size dynamically, just set the flow layout property to any arbitrary value:
let flowLayout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
flowLayout.estimatedItemSize = CGSize(width: 0, height: 0)

Swift, Change width of UICollectionViewCell and UILabel(inside the cell) programmatically

I've set the width of a cell(UICollectionViewCell) to be equal to the width of the UICollectionView and I'm trying to do exactly the same thing with the UILabel that is included inside that cell. I think the code below explains exactly what I'm trying to achieve. So i've read some question here in SO and also a couple of tutorials but I'm still not sure how I can achieve this.
In a couple of questions it was saying about using collectionViewLayout but I'm really struggling on how to use it within my code. Any ideas? Thank you!
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("myCell", forIndexPath: indexPath) as LocationViewCell
cell.locationLabel.text = "Hello there!"
// Set cell & label width to 100%
let collectionViewWidth = self.collectionView.bounds.size.width
cell.frame.size.width = collectionViewWidth // Works
cell.locationLabel.frame.size.width = collectionViewWidth // Does NOT
Update 1
So I added the following:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
// Set cell width to 100%
let collectionViewWidth = self.collectionView.bounds.size.width
return CGSize(width: collectionViewWidth, height: 35)
}
What happens is that when the view is loaded the UILabel's width is still small. If I go to another view and then return back then it's 100%. So I have to do something in the viewDidLoad() right? I'm already using self.collectionView.reloadData() but I guess that's only for data.
Update 2
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("locationCell", forIndexPath: indexPath) as LocationViewCell
cell.locationLabel.text = "Hello UILabel"
// Set cell width to 100%
let collectionViewWidth = self.collectionView.bounds.size.width
cell.frame.size.width = collectionViewWidth
cell.locationLabel.frame.size.width = collectionViewWidth
return cell
}
It doesn't work because by the time this method is called, the collection view already knows how big the cell should be because it has got it from the flow delegate method:
optional func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
This is where you should be setting the size of your cells.

Resources