UICollectionView with 2 separate design - ios

I have UICollectionView and the selected cell should like the yellow one in pic. So how to have a separate design for the selected cell and how to draw that curve above it ?
Shall I use 2 separate UICollectionViewCell for this ? Or there is any alternate way to reuse the same cell on selection.

Shall I use 2 separate UICollectionViewCell for this ?
That's one way to go. Do this if there are more differences than just the one you described.
Or there is any alternate way to reuse the same cell on selection.
Sure, you can do that. Look at the two cells in your illustration, but consider that the grey part above each one as part of the cell. The black rectangle and yellow bulging rectangle are simply two different images that you draw in the background of the cell, and you can configure the same type of cell either way simply by changing that image. This is a good approach if other aspects of the cell, like positions of labels and such, are the same between both cells.

If this is the only different between that you want to make after selection, I think that there is no need to create two different UICollectionViewCells, instead, you need to keep a reference on indexpath.row(s) of selected cell(s) and check if this is the selected row, change/add a new background image.
For example:
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
// here is the variable that should save the current selected row...
private var selectedRow: Int?
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// let's consider that you have a custom cell called "MyCustomCell"
// which has "backgroundImage" property...
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myCellID", for: indexPath) as! MyCustomCell
// checking based on the selected row
cell.backgroundImage = indexPath.row == selectedRow ? UIImage("yellow") : UIImage("default")
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedRow = indexPath.row
collectionView.reloadData()
}
}
Note that if you want to check on more than one row, you should declare a selectedRows as an array (or maybe as a set) of Ints.
Hope it helps.

Related

Unable to dequeue single cell in collection view

I have come across a very disturbing issue in which I am not able to dequeue only a single cell in collectionView. When there are more than one item in collectionView, it works fine but soon as I have an array with a single item, it doesn't show any cell. I am not able to figure it out what’s wrong with my code.
extension collectionview: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! UICollectionViewCell
return cell
}
}
The number returned for numberOfItemsInSection should be the total number of objects you want to show, so if you have an array of objects for example, you would want to return the count of that array. Since it's currently set to one, only one item will be shown. If you change that to match the number of items you're displaying, then the collectionView should cooperate.
First:
the returned of numberOfItemsInSectionwill be array.count for Example
var array = [model]()
Then in cellForItemAt you should configure your cell with data so
Add this block in your cell class
func configureCell(_ data: model) {
// code for cell view
}
Finally cellForItemAt will be
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! UICollectionViewCell
cell.configureCell(array[indexPath.item])
return cell
}
I hope this be helpful for you.
Needless to say, collection views work fine with only one cell. Your code is insufficient to manifest the problem you describe.
But here are a few observations/suggestions:
Add breakpoint or debugging log statement in numberOfItemsInSection and make sure it’s getting called. If not, then you likely have one of a variety of different possible problems:
Make sure you set the “data source” for the collection view (either in IB or programmatically). Merely conforming to UICollectionViewDataSource is not sufficient: You actually have to set the data source of the collection view.
Make sure you set the base class for your storyboard scene to be the view controller in question.
Make sure you put these UICollectionViewDataSource methods in the right object.
I notice that you've put these methods as an extension to collectionview.
Class names should, as a matter of convention, always start with upper case letter.
Usually the UICollectionViewDataSource methods are put in the view controller (or, less common, a dedicated object that you'll vend and to which keep your own strong reference). I'm not sure what to make of your class named collectionview.
But, in short, there is no problem having collection views with only one cell. Your problem rests elsewhere.

How to save the status of a cell so when I scroll or leave the page it doesn't refresh the cells?

I'm trying to make a store for a game where you can buy different colors of balls. I'm using a UICollectionView with all white balls to begin with, when I click a cell, it changes the white ball image to a colored ball image (EDIT: an image from a pre made array of colored images). when I scroll down and scroll back up, the cells I selected are reset to the white ball image. I don't want this obviously.
I've tried using the method already built into the UICollectionView class with didSelectItemAt but when I scroll down and back up it gets all messed up (When i select a cell a different one's image is changed not the correct one). I've tried using isSelected in the collectionViewCell class but I can't get the indexpath in here so I can't save which cells are selected.
override var isSelected: Bool{
didSet{
if self.isSelected
{
textImage.image = images[indexPath.item] // I don't know what to put here I don't have the indexPath
}
else
{
textImage.image = #imageLiteral(resourceName: "circleWhite")
}
}
}
Any help is great, I am fairly new to coding in Xcode so some explanation of what to do here is very much appreciated.
EDIT: I have an array of images that should be the store, not just one different color, multiple colors. When I click on a cell, it should access the image in the corresponding index in the array and use that image to replace the white circle.
Did the same thing in our code.Below is the solution for this.
1.Take an array of tuple to maintain selected status and specific colors or what ever you want.
var arrColor = [(isSelected:Bool,color:UIColor)]()
2. Now do the below code on cellForItemAt.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let collectionViewCell = self.iconCollectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as? EditDeviceCollectionViewCell else { return UICollectionViewCell() }
if arrColor[indexPath.item].isSelected{
arrColor[indexPath.item].color = .white
}else {
arrColor[indexPath.item].color = .black
}
return collectionViewCell
}
3.Now write the data source method and use below for color
//MARK:- UICollectionViewDelegate
extension yourViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
arrColor[indexPath.item].isSelected = true
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
arrColor[indexPath.item].isSelected = false.
}
}
Happy Coding 😊
When you scroll off the screen the cells get prepareForReuse called on them. What you need to do is store the state of the color somewhere else - like on the collectionView or a viewModel. And when cellForRow is called you pull the color to show for that row from the saved state variable.
Essentially what’s happening is the cells are being reused when they go off screen to save memory. So when you scroll back to them they are re-created often with the state of another cell since cells are reused.

In UICollectionView, the order changes automatically

I have a big problem.
I scrolled in the UICollectionView.
When the drawing area is scrolled too much, the arrangement order has become disjointed.
I do not want to change the order even though scrolling.
What should I do?
help me.
let titles = ["1","2","3","4","5","6","7","8", "9", "10", "11", "12"] //titles
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = testCollectionView.dequeueReusableCell(withReuseIdentifier: "TestCell", for: indexPath) as! TestCell //customCell
let testTitle = cell.contentView.viewWithTag(2) as! UILabel
testTitle.text = titles[indexPath.row]
return cell // yeah, all done. is these code is normal. right? But when i scroll this UICollection, change the order in Outside drawing area.
}
You should definitely not be using viewWithTag. The reason for this is because cells are dequeued, which means that cells that have been taken off screen by scrolling are reused for the cells which are about to come on screen. This is done to save memory. Sometimes the problem with the order can be because previously used cells are not updated quick enough when they are presented to the user. The solution to this if you are using a network request can be to firstly to streamline your request and then cache any heavy data that is returned, such as images.
Your code should not be causing the cells to change order. I'm wondering if you have a problem with using viewWithTag. That's a fairly fragile way to find views in a collection view/table view cell.
You already have a custom cell type. I would suggest creating an outlet to your label in your custom cell type, and referencing the label that way:
class TestCell: UICollectionViewCell {
//(Connect this outlet in your cell prototype in your Storyboard
#IBOutlet titleLabel: UILabel!
}
//And in your view controller's cellForItemAt method...
cell.titleLabel.text = titles[indexPath.row]

update UITableViewCell when UICollectionViewCell is selected

I am creating a project where I have a lot of options for user to select among different plans.
So I created a UITableViewCell, put UICollectionView inside with one UICollectionViewCell as one plan.
Now user can select only one plan out of all.
Let's say the user selects the second plan, I need to update the price on a button which is in UITableViewCell.
I know we can reload tableCell and update. But is there any other way to do except reloading UITableViewCell
Let me know the best way to do that except reloading whole tableviewcell
In didselect method of Uicollectionview, get the tableviewcell of price displaying and update the button.
CollectionView didSelectItem method
{
NSIndexPath *indexpathoftableviewcell = [NSIndexPath indexpathForRow:1 inSection:0];
UITableViewCell *priceCell = [tableView cellForRowAtIndexPath: indexpathoftableviewcell];
UIButton *priceBtn = (UIButton*)[priceCell viewWithTag:tagOfBtn];
[priceBtn setTitle:#"43432" forControlState:UIControlStateNormal];
}
Assuming your UITableViewCell loads the UICollectionView inside it, so the obvious choice for UICollectionView's DataSource and Delegate should be your UICollectionViewCell.
So in your TableView Cell you can simply access the label that you need to update with price as shown below
extension MyTableViewCell : UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 100
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let someCell : MyCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "your_identifier", for: indexPath) as! MyCollectionViewCell
return someCell
}
}
extension MyTableViewCell : UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! MyCollectionViewCell
self.priceLabel.text = "\(cell.value)"
}
}
here I am assuming that your CollectionViewCell is a custom cell and has a property named value where it store the price of plan
Accommodate the Plans (UICollectionView) & Price (UILabel or UIButton) in the UITableViewCell rather than making it as separate cells
Here is the reason behind it.
Both Price and Plans are tightly coupled. It means, When you change plan the respective price should come and this requirement may grow in future like adding some more views. If you accommodate the entire part in one UITableViewCell, it will become very easy. This way you can manage your design future proof.
regarding the question, the price is just like one more IBOutlet in the CustomTableViewCell. Just update the price in
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {}

Swift - separate views have subviews that are sharing the same memory?

new to swift. I have a nested CollectionView from one viewcontroller. The main viewcontroller has 7 collectionviewcell ("Level1Cell" in the code below). Each time I click a button or trigger an event, I want the collectionView to reload with the new data.
func eventHandler() {
// updates data
myCollectionView.reloadData()
}
Then, after it calls reload, it will call the reload again on each of the the nested CollectionViewCell.
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Level1Cell", for: indexPath) as! Level1Cell
cell.appsCollectionView.reloadData()
return cell
}
The problem is, let say I want to, for the first cell, set a particular row some text.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if(self.index == 0 && indexPath.row == 30){
rightCell.textLabel.text = "asdasd"
}
The fourth "Level1Cell" cell somehow has its label set also at the 30th row, but not the second and third. After stepping through the debugger, I realize that the cells, after reloading, the fourth cell "Level1Cell" is set to have the the same memory address as the first cell ( why does reload do this - shouldn't it allocate a new memory for each "Level1Cell"? - how can I get around this). Also, should I not use reload to update the data in the view and nested view of those from the view controller?
Thanks!
UIcollectionview will reuse cells. You must provide needed data for row in method
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
Or just clear previous data at cell.

Resources