I will create a carousel using UICollectionView.
And I want the cells outside the bounds of UICollectionView would be visible.
So I added this code because I thought it's an easy problem.
collectionView.isPagingEnabled = true
collectionView.clipsToBounds = false
But, Of course, some cells outside the bounds were hidden by reuse.
Is there anyway to use UICollectionView without reuse, or to adjust the reuse range, or ...etc?
Thanks in advance.
yes you can in fact use UICollectionView without reuse.
in collectionview's datasource function :
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
instead of using
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CollectionViewCell", forIndexPath: indexPath) as! CollectionViewCell
and returning the cell, you can create your own cell without calling collectionView's dequeue function and return this newly created cell
the downside to this is everytime your collectionview need a new cell, it will creates a new cell and not reusing the one you already had, resulting in overtime increasing memory hogging cells
to solve this, you can arrange an array that keep track of which cell to use on which index, and fetch cell from this array (if its available) or create one when its not and append it to the array
Related
My UICollectionViewCell is supposed to change its appearance when it comes into view (just a simple alpha/opacity change on one of the subviews). My code works fine when I scroll (vertically) slowly, but my attempt to access the cell (using cellForItemAt) returns nil when I scroll very quickly. Debugging + research reveals that cellForItem returns nil when the cell object isn't visible yet (even if the object is created), which is what happens when I scroll quickly.
What's the best way to to access the cell object when it's not visible yet?
From your description I'd say that the ideal way to access those cells is in function collectionView:willDisplayCell:forItemAtIndexPath:
That function tells you that the specified cell is just about to be displayed in the collection view.
So you can use it like this:
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
guard let cell = cell as? YourCell else { return }
// change alpha etc...
}
In my tableview, every cell will be different and determined by a JSON response from server. And there will be infinite possibilities. So defining a prototype for each type of cell is not possible.
For example, one cell will have labels and buttons, another cell have images and buttons in different orders.
How to achieve this dynamic structure in tableview cells?
Currently what I am doing is: adding views as subview in cellForRowAtIndexPath but scrolling is very laggy this way.
How to achieve this without affecting performance this much
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! MyCell
for myview in data[indexPath.row].myviews{
cell.addSubview(myview)
}
return cell
}
If you're using a table view then your content is going to scroll vertically, right?
There is a physical limit to the amount of UI that you can put horizontally. Limited by the screen size.
So I'm guessing your UI parts are being laid out vertically in the cell?
So instead of laying out a button, label, image, another button, and a text field vertically in a cell...
Create a cell type called ButtonCell, LabelCell, ImageCell, MultiLineLabelCell, TextFieldCell, etc...
So now, instead of creating one cell with all these elements added. You instead create multiple cells each containing one type of UI. Now you can dequeue your cells in any particular order (driven by your JSON) and won't lose the performance.
The only solution I see is to have empty cell and add/remove subviews as needed. But you should add new subviews to a cell only if you did not add them before.
For example:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! MyCell
if cell.contentView.viewWithTag(1) == nil {
let label = UILabel()
label.tag = 1
cell.contentView.addSubview(label)
}
let label = cell.contentView.viewWithTag(1)
// label config there
return cell
}
Also don't forget to add subviews to cell's contentView not to cell itself.
I have cell of different width and horisontal UICollectionView, but often
when I dequeue cell:
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath)
it uses the same cell with the same object address.
Update:
I mean that UICollectionView uses the same cell for all IndexPath . In debug I see the address of the cell and it's the same all the time. But I think that it's the way, UICollectionView implemented, the bug was in another point, but anyway it's strange
Reusable cells come into picture when there are more cells than to fit the screen. You just have 2 fresh objects of cells with 2 indexPath rows that are visible on the screen.
Try checking the cellForItemAtIndexPath wherein you configure or modify the cell's data after you instantiate them, you would have given same data to show in both cells or you wouldn't have changed the default values for the cell's view's UI elements which my cause the data to repeat.
I am making a menu, where I know the cells upfront. Therefore I create each cell UICollectionViewCell() in code. That use to work in UITableView, but in UICollectionView it gives error in cellForItemAt:
does not have a reuseIdentifier - cells must be retrieved by calling
-dequeueReusableCellWithReuseIdentifier:forIndexPath:'
Ok, so when I create each UICollectionViewCell(Custom super cell). It wants cell identifier, no problem. But I also wants IndexPath.
So the question is; how can I create UICollectionViewCell upfront?
Hope you can understand.
Collection view requires that you always dequeue views, rather than
create them explicitly in your code. There are two methods for
dequeueing views.
Use the
dequeueReusableCell(withReuseIdentifier:for:)
to get a cell for an item in the collection view.
Use the
dequeueReusableSupplementaryView(ofKind:withReuseIdentifier:for:)
method to get a supplementary view requested by the layout object.
Before you call either of these methods, you must tell the collection
view how to create the corresponding view if one does not already
exist. For this, you must register either a class or a nib file with
the collection view. For example, when registering cells, you use the
register(_:forCellWithReuseIdentifier:) or
register(_:forCellWithReuseIdentifier:) method. As part of the
registration process, you specify the reuse identifier that identifies
the purpose of the view. This is the same string you use when
dequeueing the view later.
In delegate method cellForItem(at:) you need to dequeue cell by passing your reuse identifer and indexpath to method dequeueReusableCell(withReuseIdentifier:for:), the returned object will be your cell.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// reference to your cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
return cell
}
If you are using stroyboards you need to set reuse identifier here
and pass it to dequeueReusableCell(withReuseIdentifier:for:) method
Here you can find more information how to use collection views.
I'm working on a UICollectionView that needs to display two pieces of information. First it needs to display an image, which I've done by setting it to the background view.
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell:UICollectionViewCell=collectionView.dequeueReusableCellWithReuseIdentifier("CellIdentifier", forIndexPath: indexPath) as UICollectionViewCell;
cell.backgroundView = UIImageView(image: introViewCollectionArray[indexPath.row].image)
return cell;
}
I have a string I also need to display above the image. The string is different for each image, but can be accessed using
introViewCollectionArray[indexPath.row].note
It looks like setting up a header for the entire UICollectionView is possible, but I don't see a way to have a header for each cell with the text content. Have I missed something, or do I need to approach this differently?
You need to add a supplementaryView or a decorationView to each cell, you do this by subclassing a collectionView layout or, more probably, a UICollectionViewFlowLayout
You may have a look at Apple Flow Layout documention and this decorationView tutorial