I have a UICollectionView which uses custom xib file for it's cells. In the cell I have an ImageView for check box. I want to change the image of the `ImageView1 when it was taped. I tried the following code snippet but, it's not working for me
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! V_Cell
if show_delete == true {
cell.img_delete.image = UIImage(named: "checked")
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! V_Cell
// Configure the cell
let data = valves_[indexPath.row]
cell.v_name.text = data
if show_delete == true {
cell.img_delete.isHidden = false
} else if show_delete == false {
cell.img_delete.isHidden = true
}
return cell
}
In this code I've tried to change the image of ImageView in didSelectItemAt.
My custom xib file is as following.
Please suggest a way to make it work. Thanks.
For these kind of problems, it is always better to keep an array which contains whether an item at a given indexpath is checked or not.
var checkArray: [Bool] = [Bool](repeating: false, count: numberOfRowsInCollectionView)
// this should be the same size as number of items in collection view
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! V_Cell
checkArray[indexPath.row] = !checkArray[indexPath.row] // if it's true, make it false and vice versa logic
collectionView.reloadItems(at: [indexPath])
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! V_Cell
// Configure the cell
let data = valves_[indexPath.row]
cell.v_name.text = data
if show_delete == true {
cell.img_delete.isHidden = false
} else if show_delete == false {
cell.img_delete.isHidden = true
}
if checkArray[indexPath.row] {
cell.img_delete.image = //image for check
}else {
cell.img_delete.image = //image for no check
}
return cell
}
Finally I solved it by the following code.
var checkArray = [Int]()
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if show_delete == true {
if checkArray.contains(indexPath.row) {
let index = checkArray.index(of: indexPath.row)
checkArray.remove(at: index!)
collectionView.reloadItems(at: [indexPath])
} else {
checkArray.append(indexPath.row)
collectionView.reloadItems(at: [indexPath])
}
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! V_Cell
// Configure the cell
let data = valves_[indexPath.row]
cell.v_name.text = data
if show_delete == true {
cell.img_delete.isHidden = false
} else if show_delete == false {
cell.img_delete.isHidden = true
}
if checkArray.contains(indexPath.row) {
cell.img_delete.image = UIImage(named: "checked_n")
} else {
cell.img_delete.image = UIImage(named: "unchecked")
}
return cell
}
I put taped element indexes in an array and reloaded the items at indexpath. if the array contains the reloaded index then it displays the check mark, if not it removes the check mark.
Related
So im trying to filter my collectionview data using another collectionview, something like this
as from the picture on i try to make a filter button so i can filter the data base on the button that user tap, and the way im trying to make that filter button is to use another collectionview, but i dont know how to implement it.
so far here's my code :
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == homeColView {
return homeVm.heroList.count
}else if collectionView == filterColView {
return homeVm.filterRoles.count
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == homeColView{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomeCollectionCell", for: indexPath) as! HomeCollectionCell
let data = homeVm.heroList[indexPath.row]
let imgUrl = URL(string: BASE_URL + data.img)
cell.heroNameLabel.text = data.localized_name
cell.heroImage.kf.setImage(with: imgUrl)
return cell
}else if collectionView == filterColView {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FilterCollectionCell", for: indexPath) as! FilterCollectionCell
cell.filterLabel.text = homeVm.filterRoles[indexPath.row]
return cell
}
return collectionView.dequeueReusableCell(withReuseIdentifier: "HomeCollectionCell", for: indexPath) as! HomeCollectionCell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView == homeColView {
let vc = HeroDetailsVc()
vc.heroDetailsVm.heroDetails = homeVm.heroList[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
collectionView.deselectItem(at: indexPath as IndexPath, animated: true)
}else if collectionView == filterColView {
}
}
can anyone help me how to make this filter with collectionview?
Thank you
Using UICollectionView to display calendar, I have made today's date selected by default and want to deselect that when any other date selected. Below is my code:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CalenderCell", for: indexPath) as! CalenderCollectionViewCell
cell.lblDayName.text = arrCalendarDays[indexPath.item]
cell.lblDate.text = arrCalendarOnlyDate[indexPath.item]
if indexPath.item == 0 {
if !isTodayDate {
cell.isSelected = true
} else {
cell.isSelected = false
}
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
isTodayDate = true
// Tried below code but not working
/* collectionViewWeekCalendar.indexPathsForSelectedItems?
.forEach {
self.collectionViewWeekCalendar.deselectItem(at: $0, animated: false) }
*/
let selectedCell:UICollectionViewCell = collectionViewWeekCalendar.cellForItem(at: indexPath)!
selectedCell.isSelected = true
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let selectedCell:UICollectionViewCell = collectionViewWeekCalendar.cellForItem(at: indexPath as IndexPath)!
selectedCell.isSelected = false
}
Problem is that, when selecting any other date, today's selected date is not getting deselect. Please guide.
the hacky and easiest way (i think)
use selected index
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CalenderCell", for: indexPath) as! CalenderCollectionViewCell
cell.lblDayName.text = arrCalendarDays[indexPath.item]
cell.lblDate.text = arrCalendarOnlyDate[indexPath.item]
if sellectedIndex == indexPath {
cell.isSelected = true
} else {
cell.isSelected = false
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedIndex = indexPath
collectionView.reloadData()
}
and at the start,
set selectedIndex = today date Index
I have a variabel selectedIndexPath, which gets the indexPath selected from previous view controller. I am getting the required backgroundColor for cell in the collection view of present view controller. But when I select another cell in the collection view, the selected cell of previous view controller remains same without being deselected. So, now I have two cells with the background color. Following is my code
var selectedIndexPath = IndexPath()
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView.allowsMultipleSelection = false
self.collectionView.allowsSelection = true
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! TestCollectionViewCell
if (indexPath == selectedIndexPath)
{
cell.backgroundColor = UIColor.white
}
else
{
cell.backgroundColor = UIColor.clear
}
collectionView.allowsMultipleSelection = false
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = UIColor.white
collectionView.allowsMultipleSelection = false
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath)
{
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = UIColor.clear
collectionView.allowsMultipleSelection = false
}
How do I deselect the cell in cellForItemAt, when I select new cell in didSelectItemAt. Thanks in advance.
First of all set allowsMultipleSelection in Interface Builder and remove all redundant occurrences of setting it in code
You have to update the selectedIndexPath variable. Manipulating the cell directly is always a bad idea.
Reloading the cells is much more reliable.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
guard selectedIndexPath != indexPath else { return }
let indexPathsToUpdate = [selectedIndexPath, indexPath]
selectedIndexPath = indexPath
tableView.reloadRows(at: indexPathsToUpdate, with: .none)
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath)
{
guard selectedIndexPath == indexPath else { return }
selectedIndexPath = IndexPath()
tableView.reloadRows(at: [indexPath], with: .none)
}
There is only one problem: If you want to have an empty selection you have to declare selectedIndexPath as optional and handle it properly.
First of all, you don't need to override didDeselect for this. All you need to do is deselect the previously selected item while selecting the current item in didSelectItem. For that you can choose to maintain states in the cell as:
func changeToSelectedState() {
self.backgroundColor = UIColor.white
}
func changeToUnselectedState() {
self.backgroundColor = UIColor.clear
}
Or you can choose to write the same in the controller itself. And then you need to perform the deselection and selection in the following manner.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
let previousSelectedCell = collectionView.cellForItem(at: selectedIndexPath)
previousSelectedCell.changeToUnselectedState()
let currentCell = collectionView.cellForItem(at: indexPath)
currentCell.changeToSelectedState()
selectedIndexPath = indexPath
}
I have UICollectionView 2 rows 10+ cells.
deselected by default. when I click it becomes selected but when I click again not deselect.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(indexPath)
let cell = collectionView.cellForItem(at: indexPath)
let collectionActive: UIImageView = {
let image=UIImageView(image: #imageLiteral(resourceName: "collectionActive"))
image.contentMode = .scaleAspectFill
return image
}()
let collectionInactive: UIImageView = {
let image=UIImageView(image: #imageLiteral(resourceName: "collectionInactive"))
image.contentMode = .scaleAspectFill
return image
}()
if cell?.isSelected == true {
cell?.backgroundView = collectionActive
}else{
cell?.backgroundView = collectionInactive
}
}
how fix that problem?
in viewDidLoad()
collectionView.allowsMultipleSelection = true;
afterword I implemented these methods
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.cellForItemAtIndexPath(indexPath) as! MyCell
cell.toggleSelected()
}
func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.cellForItemAtIndexPath(indexPath) as! MyCell
cell.toggleSelected()
}
finally in my class
class MyCell : UICollectionViewCell {
func toggleSelected ()
{
if (selected){
backgroundColor = UIColor.redColor()
}else {
backgroundColor = UIColor.whiteColor()
}
}
}
For Swift 5 +
in viewDidLoad()
collectionView.allowsMultipleSelection = true
afterword I implemented these methods
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! MovieDetailsDateCollectionViewCell
cell.toggleSelected()
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! MovieDetailsDateCollectionViewCell
cell.toggleSelected()
}
In TableView Cell class
class MyCell : UICollectionViewCell {
func toggleSelected ()
{
if (isSelected){
backgroundColor = .red
}else {
backgroundColor = .white
}
}
}
If you don't want to enable multiple selection and only want one cell to be selected at a time, you can use the following delegate instead:
If the cell is selected then this deselects all cells, otherwise if the cell is not selected, it selects it as normal.
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
let cell = collectionView.cellForItem(at: indexPath) as! CustomCell
if cell.isSelected {
collectionView.selectItem(at: nil, animated: true, scrollPosition: [])
return false
}
return true
}
According to UICollectionView class doc, you can use:
var selectedBackgroundView: UIView? { get set }
You can use this view to give the cell a custom appearance when it is selected. When the cell is selected, this view is layered above the backgroundView and behind the contentView.
In your example in the cellForItem(at indexPath: IndexPath) -> UICollectionViewCell? function you can set:
cell.backgroundView = collectionInactive
cell.selectedBackgroundView = collectionActive
If the cell is selected, just set cell.isSelected = false in shouldSelectItemAt delegate and in a DispatchQueue.main.async { } block. So the state is actually changed to false (very) soon after the shouldSelectItemAt has been executed.
It may look like a hack but it actually works.
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
if let cell = collectionView.cellForItem(at: indexPath), cell.isSelected {
DispatchQueue.main.async { // change the isSelected state on next tick of the ui thread clock
cell.isSelected = false
self.collectionView(collectionView, didDeselectItemAt: indexPath)
}
return false
}
return true
}
Please let me know if you find/know any cons to do this. Thanks 🙏
In iOS 14 and newer, you can set the backgroundConfiguration property of a cell. Once set, all necessary visual effects for selecting and deselecting works automatically. You can use one of the preconfigured configurations, like this:
cell.backgroundConfiguration = .listSidebarCell()
…or create a UIBackgroundConfiguration object from scratch. You can also change a preconfigured configuration before applying.
More info here: https://developer.apple.com/documentation/uikit/uibackgroundconfiguration
1.I am using two collection views for two different designs and . subclasss the two different cells but i am getting error at below method
it showing missing return function but i have give return the cells
2. shall i use singel collectinon view sub class for both different layoutas has given through storyboard
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
if collectionView == similarProductCollection || collectionView == moreShpsColectionView
{
var productCell = SimilarProductCollectionViewCell()
if collectionView == similarProductCollection
{
productCell=similarProductCollection.dequeueReusableCell(withReuseIdentifier: "collecCell", for: indexPath) as! SimilarProductCollectionViewCell
return productCell
}
else{
// productCell=NSNull
var shopsCell = MoreShpsCollectionViewCell()
shopsCell=moreShpsColectionView.dequeueReusableCell(withReuseIdentifier:"moreCell", for: indexPath) as! MoreShpsCollectionViewCell
return shopsCell
}
}
}
this if false:
if collectionView == similarProductCollection || collectionView == moreShpsColectionView
{
}
returns nothing
Example:
func someFunction() -> Bool {
let something = true
if(something){
return true
}
}
This will produce an Error even if you are 100% sure it will work. You would need to change it to:
func someFunction() -> Bool {
let something = true
if(something){
return true
}else{
return false
}
}
Use like below :
showing missing return function mean you are not return any cell in outer block so write condition like below.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
if collectionView == similarProductCollection
{
var productCell = SimilarProductCollectionViewCell()
productCell=similarProductCollection.dequeueReusableCell(withReuseIdentifier: "collecCell", for: indexPath) as! SimilarProductCollectionViewCell
return productCell
}else{
// productCell=NSNull
var shopsCell = MoreShpsCollectionViewCell()
shopsCell=moreShpsColectionView.dequeueReusableCell(withReuseIdentifier:"moreCell", for: indexPath) as! MoreShpsCollectionViewCell
return shopsCell
}
}
Use like below, its may be work.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
var cell: UICollectionViewCell?
if collectionView == similarProductCollection
{
cell = similarProductCollection.dequeueReusableCell(withReuseIdentifier: "collecCell", for: indexPath) as! SimilarProductCollectionViewCell
}else{
cell = moreShpsColectionView.dequeueReusableCell(withReuseIdentifier:"moreCell", for: indexPath) as! MoreShpsCollectionViewCell
}
return cell
}