I have a UICollectionViewCell with an item Desk. Desk has a Bool property that I want to change when a cell is tapped.
I'm trying to better familiarize myself with the delegate pattern, and want to avoid using something like a transparent button overlaid on the cell. I thought it would work to assign the cell's delegate in cellForItemAt and trigger the delegate method in didSelectItemAt, but the cell's delegate is nil when I check in didSelectItemAt.
Struct:
struct Desk: Equatable, Codable {
var name: String
var wasTouched: Bool = false
}
Protocol:
protocol TouchTheDesk: AnyObject {
func youTouchedIt(cell: DeskCell)
}
Cell:
import UIKit
class DeskCell: UICollectionViewCell {
#IBOutlet weak var deskLbl: UILabel!
weak var delegate: TouchTheDesk?
var desk: Desk? {
didSet {
updateViews()
}
}
func updateViews() {
self.deskLbl.text = desk?.name
}
}
Conform VC to Protocol and define delegate method:
extension NotAShoppingListVC: TouchTheDesk {
func youTouchedIt(cell: DeskCell) {
if cell.desk?.wasTouched != nil {
cell.desk!.wasTouched = !cell.desk!.wasTouched
} else {
cell.desk!.wasTouched = true
}
collectionView.reloadData()
}
}
cellForItemAt:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as? DeskCell else {return UICollectionViewCell()}
cell.desk = deskDealer.desks[indexPath.item]
if cell.desk!.wasTouched { //assigned on line above
cell.backgroundColor = .red
} else {
cell.backgroundColor = .green
}
cell.delegate = self
return cell
}
didSelectItemAt:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as? DeskCell else {return}
#warning("delegate is nil")
cell.delegate?.youTouchedIt(cell: cell)
}
edit: If I call the delegate method directly in didSelectItemAt and pass in the cell, the indexPath for the cell is nil in the delegate method
Inside didSelectItemAt Replace
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as? DeskCell else {return}
with
guard let cell = collectionView.cellForItem(at: indexPath) as? DeskCell else {return}
when you use dequeueReusableCell outside of cellForItemAt it will return a different cell other than the clicked one
Related
I'm working on an iOS app which contain a collectionview, inside this collectionview I have a button, as follow:
ViewController
-UICollectionView(myColl)
--UICollectionViewCell
---UIButton (myButton)
-UIView (myView)
What I want to do is to show myView under myButton when I tapped it, what I try is:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TestCell",
for: indexPath) as! TestCell
cell.myButton.addTarget(self, action: #selector(self.showEditView), for: .touchUpInside)
}
and in showEditView()
#objc func showEditView(sender:UIButton!) {
let position: CGPoint = sender.convert(CGPoint.zero, to: self.myColl)
myView.center = position
}
But that wasn't worked, what can I do to get that?
#objc func showEditView(sender:UIButton!) {
let position: CGPoint = sender.convert(CGPoint.zero, to: self.collectionview)
let indexPath = self.collectionview.indexPathForItem(at: position)
if indexPath != nil {
myView.center = position
//if your view is hidden
myView.isHidden = false
}
}
Actually you can use protocol in your UICollectionViewCell for it. It's healthier than use forge using.
protocol YourProtocolDelegate {
func showEditView()
}
class YourCVCell: UICollectionViewCell {
var delegate: YourProtocolDelegate!
var button = UIButton()
override init(frame: CGRect) {
button.addTapGestureRecognizer {
self.delegate.showEditView()
}
}
}
Of course you must take inherit from your protocol to your ViewController class
class YourViewController: UIViewController, YourProtocolDelegate {
}
After that you can reference your cell's protocol to your viewcontroller.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TestCell",
for: indexPath) as! TestCell
cell.delegate = self
}
I am using a UICollectionView inside UITableViewCell. I am able to select the cells inside the UICollectionView. But when i try to get the UICollectionView or selected cells, the result is always null.I have been stuck on this for a long time. i included my code below for your reference.
class WeekDaysSelCell: UITableViewCell,UICollectionViewDelegate, UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {
var weekdays = ["S", "M", "T", "W", "T", "F", "S"]
var weekdaysSelected = [String]()
#IBOutlet var weeklyDaysColView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
self.weeklyDaysColView.delegate = self
self.weeklyDaysColView.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 7
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell : WeekDaysCollCell = weeklyDaysColView.dequeueReusableCell(withReuseIdentifier: "weekday", for: indexPath) as! WeekDaysCollCell
cell.weekDayLabel.text = weekdays[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell : WeekDaysCollCell = self.weeklyDaysColView.cellForItem(at: indexPath) as! WeekDaysCollCell
if (cell.backgroundColor == UIColor.gray) {
cell.backgroundColor = UIColor.clear
weekdaysSelected.removeAll { $0 == String(indexPath.row)}
//print("Removed from weekdaysSelected:", indexPath.row)
} else {
cell.backgroundColor = UIColor.gray
cell.isSelected = true
//weeklyDaysColView.selectItem(at: indexPath, animated: true, scrollPosition: [])
weekdaysSelected.append(String(indexPath.row))
//print("Added to weekdaysSelected:", indexPath.row)
}
}
}
// Trying to get the collection view from inside a willMove(toParent parent: UIViewController?) method.
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
if parent == nil
{
if let delegate = self.delegate {
print("Inside If condition")
// Code that i use to get the cell
let cell3 = tableView.dequeueReusableCell(withIdentifier: "cell3") as! WeekDaysSelCell
print(cell3.weekdaysSelected)
print(cell3.weeklyDaysColView.indexPathsForSelectedItems)
// Trying to pass selected cells
//delegate.repeatCustomSelection(selectedIdx: String(lastSelection.row),repeatCustomSel: repeatCustomSelection)
}
}
}
You are trying to get a reusable cell in willMove(toParent parent: UIViewController?) , this is not going to return you a expected cell.
You need to get the cell , using a indexPath .
func cellForRow(at indexPath: IndexPath) -> UITableViewCell?
#andyPaul, is right you are generating the new cell in willMove(toParent parent: UIViewController?). Instead of that you have to pass the indexpath pf collection view when ever user selected any cell to your controller from the tableView Cell Class.
Now What is TypeAlias you can read from this link about type alias:- https://www.programiz.com/swift-programming/typealias
Create the typeAlias on above of your tableViewCell Class like this:-
typealias closureBlock = (_ isCapture : AnyObject?) ->()
class tableViewCellClass: UITableViewCell {
var callBack: closureBlock?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
Just Go to CollectionView didSelectItemAt Method and use this code after your coding
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell : WeekDaysCollCell = self.weeklyDaysColView.cellForItem(at: indexPath) as! WeekDaysCollCell
if (cell.backgroundColor == UIColor.gray) {
cell.backgroundColor = UIColor.clear
weekdaysSelected.removeAll { $0 == String(indexPath.row)}
//print("Removed from weekdaysSelected:", indexPath.row)
} else {
cell.backgroundColor = UIColor.gray
cell.isSelected = true
//weeklyDaysColView.selectItem(at: indexPath, animated: true, scrollPosition: [])
weekdaysSelected.append(String(indexPath.row))
//print("Added to weekdaysSelected:", indexPath.row)
}
guard let callBackClosure = self.callBack else {
return
}
callBackClosure(indexPath as AnyObject)
// You can pass any value here either indexpath or Array.
}
}
Now you have to initialise this closure so that it can check whether in which controller it will return the value when you assign the value from CollectionView didSelectItemAt Method.
Go to your ViewController Class where you have added the tableview and their datasources.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//You have to pass your tableview Cell Instance here and their reuse Identifier
let cell = tableView.dequeueReusableCell(withIdentifier: "tableViewCellClass", for: indexPath) as! tableViewCellClass
cell.callBack = { [weak self] (selectedIndexPath) -> ()in
// You will get the current selected index path of collection view here, Whenever you pass any index path from collectionView did SelectItem Method.
print(selectedIndexPath)
}
return cell
}
I have a horizontally scrollable UICollectionView with three cells each of which are different subclasses of UICollectionViewCell. Each one of these cells contains a UITableView.
Inside of the first two cells, my table view cells are the same subclasses of UITableViewCell and have just a UIImageView. I use it to set its backgroundColor. Inside of the third cell, my table view's cells are different subclasses of UITableViewCell than in the previous two. They have both a UILabel and a UIImageView. The label has some dummy text, and I set imageView's backgroundColor to some color, again.
In order to follow MVC pattern, I use my UIViewController as a data source and a delegate for both collection view, and table view. Here is the code of UIViewController:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let collectionViewCellId = "collectionViewCell"
let tableViewCellId = "tableViewCell"
let collectionViewCellId2 = "collectionViewCellId2"
let collectionViewCellId3 = "collectionViewCellId3"
let tableViewCellDif = "tableViewCellDif"
var collectionViewIndex: Int?
#IBOutlet weak var collectionView: UICollectionView! {
didSet {
collectionView.delegate = self
collectionView.dataSource = self
collectionView.isPagingEnabled = true
}
}
//MARK: UITableViewDataSource
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let colors: [UIColor] = [.red, .green, .purple, .orange, .blue]
let colors2: [UIColor] = [.blue, .brown, .yellow, .magenta, .cyan]
if collectionViewIndex == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellId, for: indexPath) as! TableViewCell
cell.colorForImageView = colors[indexPath.row]
return cell
} else
if collectionViewIndex == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellId, for: indexPath) as! TableViewCell
cell.colorForImageView = colors2[indexPath.row]
return cell
} else
if collectionViewIndex == 2 {
let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellDif, for: indexPath) as! TableViewCellDifferent
cell.colorForImageView = colors2[indexPath.row]
return cell
} else {
return UITableViewCell()
}
}
}
//MARK: UICollectionViewDataSource
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let identifier: String
if indexPath.item == 0 {
identifier = collectionViewCellId
} else if indexPath.item == 1 {
identifier = collectionViewCellId2
} else if indexPath.item == 2 {
identifier = collectionViewCellId3
} else {
identifier = ""
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath)
return cell
}
}
//MARK: UICollectionViewDelegate
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath.item == 0 {
let cell = cell as! CollectionViewCell
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 0
}
if indexPath.item == 1 {
let cell = cell as! CollectionViewCell2
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 1
}
if indexPath.item == 2 {
let cell = cell as! CollectionViewCell3
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 2
print (collectionViewIndex)
}
}
}
//MARK: UICollectionViewDelegateFlowLayout
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let layout = collectionViewLayout as! UICollectionViewFlowLayout
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
return CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
}
}
As I stated in a title of the question, nothing happens on a background thread. I, basically, only set the backgroundColor of table view's cells.
The problem is that inside the collection view's third cell (and only inside of there), my table view dequeues its cells only after a minor scroll or tap happens. Here is how it looks like:
I can't figure out why this happens. Maybe, this happens because inside of the third cell of the collection view, my table view's cells are instances of different subclass than inside of the first two?
EDITED
I could solve the problem by reloading the table view before before showing the collection view's each cell but I'm not sure that this is the most efficient solution. Here is the code:
//MARK: UICollectionViewDelegate
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath.item == 0 {
let cell = cell as! CollectionViewCell
cell.tableView.dataSource = self
cell.tableView.delegate = self
cell.tableView.reloadData()
collectionViewIndex = 0
}
if indexPath.item == 1 {
let cell = cell as! CollectionViewCell2
cell.tableView.dataSource = self
cell.tableView.delegate = self
cell.tableView.reloadData()
collectionViewIndex = 1
}
if indexPath.item == 2 {
let cell = cell as! CollectionViewCell3
cell.tableView.dataSource = self
cell.tableView.delegate = self
cell.tableView.reloadData()
collectionViewIndex = 2
}
}
}
If you know a better way, I would appreciate your help.
I gave this a try, and saw the same results. So, I moved your collection view cell "setup" code from willDisplay cell: to cellForItemAt and it fixed the problem.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: collectionViewCellId, for: indexPath) as! CollectionViewCell
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 0
return cell
}
if indexPath.item == 1 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: collectionViewCellId2, for: indexPath) as! CollectionViewCell2
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 1
return cell
}
// if we get here, indexPath.item must equal 2
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: collectionViewCellId3, for: indexPath) as! CollectionViewCell3
cell.tableView.dataSource = self
cell.tableView.delegate = self
collectionViewIndex = 2
return cell
}
Now, since you're not showing your code for your tableview cells, it's possible there might be another issue, but this worked for me:
class TableViewCell: UITableViewCell {
#IBOutlet var theImageView: UIImageView!
var colorForImageView: UIColor = UIColor.gray {
didSet {
self.theImageView.backgroundColor = colorForImageView
}
}
}
You can try to dequeue CollectionViewCell or TableViewCell explicitly on the main thread
DispatchQueue.main.async {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath)
}
or
DispatchQueue.main.async {
let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellDif, for: indexPath) as! TableViewCellDifferent
cell.colorForImageView = colors2[indexPath.row]
}
It could help to wake up the main thread
But in general, it would be much easier if a data source for the table view was inside collection view cell
In my view controller I want to have two collection views. After trying this, I got a Sigabrt Error and my app crashes. I am predicting that the problem is because I am assigning the datasource of these collection views to self. I can be wrong, here is my code:
In view did load, i set the datasource of the collection views:
#IBOutlet var hashtagCollectionView: UICollectionView!
#IBOutlet var createCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
createCollectionView.dataSource = self
hashtagCollectionView.dataSource = self
}
Then I create an Extension for the UICollectionViewDataSource
extension CategoryViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
var returnValue = 0
if collectionView == hashtagCollectionView {
// Return number of hashtags
returnValue = hashtags.count
}
if collectionView == createCollectionView {
// I only want 3 cells in the create collection view
returnValue = 3
}
return returnValue
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var return_cell: UICollectionViewCell
// Place content into hashtag cells
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "hashtagCell", for: indexPath) as! TrendingTagsCollectionViewCell
cell.hashtagText = hashtags[indexPath.row]
// Place content in creators cell
let createCell = collectionView.dequeueReusableCell(withReuseIdentifier: "createCell", for: indexPath) as! CreateCollectionViewCell
createCell.text = creators[indexPath.row]
createCell.image = creatorImages[indexPath.row]
// Is this the right logic?
if collectionView == hashtagCollectionView {
return_cell = cell
} else {
return_cell = createCell
}
return return_cell
}
}
This is crashing because your if statement in collectionView(_:cellForItemAt:) doesn't quite cover enough ground.
While you're right that you need to return a different cell based on what collection view is asking, you can't call dequeueReusableCell(withReuseIdentifier:for:) twice with different identifiers like that. Since you ask the same collection view both times, it's very likely that one of the identifiers isn't registered with that collection view.
Instead, you should expand the if to cover just about the entirety of that method:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == hashtagCollectionView {
// Place content into hashtag cells
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "hashtagCell", for: indexPath) as! TrendingTagsCollectionViewCell
cell.hashtagText = hashtags[indexPath.row]
return cell
} else if collectionView == createCollectionView {
// Place content in creators cell
let createCell = collectionView.dequeueReusableCell(withReuseIdentifier: "createCell", for: indexPath) as! CreateCollectionViewCell
createCell.text = creators[indexPath.row]
createCell.image = creatorImages[indexPath.row]
return cell
} else {
preconditionFailure("Unknown collection view!")
}
}
That only tries to dequeue a cell once, depending on which collection view is asking, and casts the returned cell to the right class.
N.B. This kind of approach works for awhile, but in the long run, you can get very long UICollectionViewDataSource method implementations, all wrapped up in a series of if statements. It might be worth considering separating out your data sources into separate smaller classes.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "hashtagCell", for: indexPath) as? TrendingTagsCollectionViewCell {
cell.hashtagText = hashtags[indexPath.row]
return cell
}
if let createCell = collectionView.dequeueReusableCell(withReuseIdentifier: "createCell", for: indexPath) as? CreateCollectionViewCell {
createCell.text = creators[indexPath.row]
createCell.image = creatorImages[indexPath.row]
return createCell
}
return UICollectionViewCell() // or throw error here
}
I think your code crash with a nil when reuse cell
You should test cellForItemAt like this
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == hashtagCollectionView {
// Place content into hashtag cells
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "hashtagCell", for: indexPath) as! TrendingTagsCollectionViewCell
cell.hashtagText = hashtags[indexPath.row]
return cell
} else {
// Place content in creators cell
let createCell = collectionView.dequeueReusableCell(withReuseIdentifier: "createCell", for: indexPath) as! CreateCollectionViewCell
createCell.text = creators[indexPath.row]
createCell.image = creatorImages[indexPath.row]
return createCell
}
}
In Table view we can put checkmark easily on cells.
But in Collection View how can we put check mark, when we select a cell (image)?
I just took a image view inside the cell and image view and put a tick mark image. My code is below.
But it's not working.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
// handle tap events
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! customCollectionViewCell
if(cell.checkMarkImage.hidden == true)
{
print("Hidden")
cell.checkMarkImage.hidden = false
}
else
{
cell.checkMarkImage.hidden = true
print("No Hidden")
}
}
//Delegate Method cellForItemAtIndexPath
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) ->
UICollectionViewCell
{
//Get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(
"pickSomecell",
forIndexPath: indexPath) as! pickSomeGridViewController
//Show Images in grid view
cell.cellImage.image = self.arrAllOriginalImages[indexPath.row]
as? UIImage
//Check Mark toggle.
cell.toggleSelected()
//return cell.
return cell
}
And in pickSomeGridViewController show checkMark image selected or not.
class pickSomeGridViewController: UICollectionViewCell{
//Outlet of cell image.
#IBOutlet var cellImage: UIImageView!
//Outlet of checkMark image.
#IBOutlet var cellCheckMarkImage: UIImageView!
//Function for select and deselect checkmark.
func toggleSelected ()
{
//If image is selected.
if (selected)
{
//Show check mark image.
self.cellCheckMarkImage.hidden = false
}
else
{
//Hide check mark image.
self.cellCheckMarkImage.hidden = true
}
}
}
I see two main problems with this code:
You use dequeueReusableCellWithReuseIdentifier method which obtains different cell from collection view cache, not the one on screen.
Use cellForItemAtIndexPath method of collection view instead.
You try to save cell's state (selected/not selected) in the cell itself. It's common mistake when working with UITableView/UICollectionView and this approach will not work. Instead, keep the state in some other place (in dictionary, for example) and restore it every time collection view calls your data source cellForItemAtIndexPath method.
var arrData = NSMutableArray()
// 1.Make a ModalClass.swift and NSArray with modal class objects like this
class CustomModal: NSObject {
//Declare bool variable for select and deselect login
var is_selected = Bool()
//you can declare other variable also
var id = Int32()
}
// 2. custom array with modal objects
override func viewDidLoad() {
super.viewDidLoad()
let arrTemp = NSArray()
arrTemp = [1,2,3,4,5,6,7,8,9,10]
for i in 0 ..< arrTemp.count{
let eventModal = CustomModal()
eventModal.is_selected = false
eventModal.id = arrTemp[i]
arrData.add(eventModal)
}
tblView.reloadData()
}
// 2. Use collection view delegate method
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let modal = arrData[indexPath.row] as! CustomModal()
modal.is_selected = true
self.arrData.replaceObject(at: indexPath.row, with: modal)
tblView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let modal = arrData[indexPath.row] as! CustomModal()
modal.is_selected = false
self.arrData.replaceObject(at: indexPath.row, with: modal)
tblView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! YourCellClass
let modal = arrData[indexPath.row] as! CustomModal
if modal.is_selected == true{
cell.imgView.image = UIImage(named:"selected_image")
}else{
cell.imgView.image = UIImage(named:"deselected_image")
}
}
#Kishor, paintcode is the third party tool through which you can do that. I have provided the link too. since by default you don't have this facility, you should make your custom behavior to achiever this. Thanks.
Swift 4
In ViewController
// make a cell for each cell index path
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourCollectionViewCellID", for: indexPath as IndexPath) as! YourCollectionViewCell
cell.someImageView.image = imgArr[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.item)!")
let cell = collectionView.cellForItem(at: indexPath) as? YourCollectionViewCell
cell?.isSelected = true
cell?.toggleSelected()
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as? YourCollectionViewCell
cell?.isSelected = false
cell?.toggleSelected()
}
In YourCollectionViewCell
class YourCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var someImageView: UIImageView!
#IBOutlet weak var checkImageView: UIImageView!
//Function for select and deselect checkmark.
public func toggleSelected() {
if (isSelected == false) {
//Hide check mark image.
self.checkImageView.image = UIImage(named: "unCheckImage")
isSelected = true
}else{
//Show check mark image.
self.checkImageView.image = UIImage(named: "CheckImage")
isSelected = false
}
}
}
Hope enjoy!!
var selectedCellIndex:Int?
take variable if you want to show selected Item after reloadData() : which is previously selected CellItem. {inspired by above answer }
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ColorCollectionCell", for: indexPath) as! ColorCollectionCell
cell.isSelected = false
if selectedCellIndex == indexPath.item {
cell.checkMarkImgView.image = UIImage(named: "icn_checkMark")
}else {
cell.toggleSelected()
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! ColorCollectionCell
cell.isSelected = true
selectedCellIndex = indexPath.item
cell.toggleSelected()
}
In CollectionViewCell u can use this method
class ColorCollectionCell: UICollectionViewCell {
#IBOutlet weak var cellimgView: UIImageView!
#IBOutlet weak var checkMarkImgView: UIImageView!
func toggleSelected() {
if (isSelected) {
self.checkMarkImgView.image = UIImage(named: "icn_checkMark")
}else{
self.checkMarkImgView.image = UIImage(named: "")
// here you can use uncheck img here i am not using any image for not selected.
}
}
}