calling functions from within cell when "didSelectItemAt" is called - ios

I have a UICollection view of things. these things can have 3 states:
- Active
- Neutral
- Inactive
Now, here is the code for the UICollectionViewCell:
class NGSelectStashCell: UICollectionViewCell {
var status: String = "Active"
#IBOutlet weak var statusImage: UIImageView!
#IBOutlet weak var bgImage: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
func changeStatus()
{
switch status {
case "Active":
status = "Neutral"
//change bgImage
case "Neutral":
status = "Inactive"
//change bgImage
case "Inactive":
status = "Active"
//change bgImage
default:
print("No Status")
}
}
}
Now, when I declare the UICollection View, I want to make it so that when the user "clicks" on the UICell it will call out the changeStatus() function. How can I do this in the Delegate/DataSource code?. Also, how do I save the "status" of the each cell (so that if I refresh the UICollectionView they don't all return to "active" ?
/*
////////// UICOLLECTIONVIEW FUNCTIONS ///////////
*/
extension NewOnlineGameVC: UICollectionViewDelegate, UICollectionViewDataSource
{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return availableStashes.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let stashCell = collectionView.dequeueReusableCell(withReuseIdentifier: "ngStashCell", for: indexPath) as! NGSelectStashCell
stashCell.titleLabel.text = availableStashes[indexPath.row]
// stashCell.bgImage make image file with the same name as the name and change bg image to it.
return stashCell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// code to toggle between active/neutral/inactive
// do I re-declare stashCell as! NGSelectStashCell? or what do I do?
}
}

Unfortunately the solution is a bit more complicated then you think. Collection views may queue and reuse their cells for performance gains. That means that a single cell may and will be used for multiple objects when scrolling. What will happen is that when you will change the state on first cell and will scroll so it is reused then this cell will preserve its state and will look as if another cell has this changed state...
So your source of truth must always be your data source. Whatever availableStashes contains it needs to also contain its state. So for instance if you currently have var availableStashes: [MyObject] = [] you can change it like this:
typealias MySource = (status: String, object: MyObject)
var availableStashes: [MySource] = []
func setNewObjects(objects: [MyObject]) {
availableStashes = objects.map { ("Neutral", $0) }
}
Now on press you need to update the object in your data source for instance:
func changeStatusOfObjectAtIndex(_ index: Int, to newStatus: String) {
availableStashes[index] = (newStatus, availableStashes[index].object)
}
So on press you do:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
changeStatusOfObjectAtIndex(indexPath.row, to: <#Your new status here#>)
UICollectionView().reloadItems(at: [indexPath])
}
This will now trigger a reload for this specific cell which you can now update like
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let stashCell = collectionView.dequeueReusableCell(withReuseIdentifier: "ngStashCell", for: indexPath) as! NGSelectStashCell
stashCell.dataObject = availableStashes[indexPath.row]
return stashCell
}
And inside the cell:
var dataObject: NewOnlineGameVC.MySource {
didSet {
titleLabel.text = dataObject.object
switch dataObject.status {
case "Active":
//change bgImage
case "Neutral":
//change bgImage
case "Inactive":
//change bgImage
default:
print("No Status")
}
}
}
I hope this clears your issue.

You can change the status of the cell once you get the reference of the selected cell.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let cell = collectionView.cellForItem(at: indexPath) as? NGSelectStashCell else {return}
cell.status = "Active"
cell.changeStatus()
}
If you want to save the status of the cell then it must be model driven i.e anything happens to the cell must be saved to the model and the same model have to be reflecte in the cell when collection view tries to reuse the previously instantiated cells.
You already have a model AvailableStash, lets use it in proper way.
struct AvailableStash {
var statusImage: UIImage?
var backgroundImage: UIImage?
var title: String?
var status: String
//Initilize properties properly
init(with status: String) {
self.status = status
}
}
Your collection view must be model driven. For eg:
class DemoCollectionView: UICollectionViewController {
var availableStashes: [AvailableStash]?
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return availableStashes?.count ?? 0
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let stashCell = collectionView.dequeueReusableCell(withReuseIdentifier: "ngStashCell", for: indexPath) as! NGSelectStashCell
let item = availableStashes[indexPath.row]
stashCell.titleLabel.text = item
// stashCell.bgImage make image file with the same name as the name and change bg image to it.
stashCell.statusImage = item.statusImage
return stashCell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let cell = collectionView.cellForItem(at: indexPath) as? NGSelectStashCell else {return}
cell.status = availableStashes[indexPath.row].status
cell.changeStatus()
availableStashes[indexPath.row].status = cell.status
}
}

Related

Print the model values in collectionview

class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
var liveModel = [LiveModel]()
#IBOutlet weak var myCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return liveModel.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = myCollectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCollectionViewCell
cell.imageViewer.image = UIImage(named: "\(liveModel[indexPath.row].image[0].src)")
return cell
}
}
struct LiveModel: Codable {
var id: Int
var name: String
var image: [Image]
}
struct Image: Codable {
var id: Int
var name: Int
var src: String
}
I have this model. I want to access "src" value from "Image" for collection view using indexpath. How can I do that?
I am fetching the images from API. I use model for that. I successfully access the value "id" & "name" from "LiveModel" but I can't understand how access the "src" value from "Image" model
Assuming you only want to show the first image, and assuming there is always at least 1 image, then your line:
cell.imageViewer.image = UIImage(named: "\(liveModel[indexPath.row].image[0].src)")
is close. Just remove the unnecessary string interpolation.
cell.imageViewer.image = UIImage(named: liveModel[indexPath.row].image[0].src)
This code will crash if the given liveModel record has no images. So if that is a possibility you should code accordingly.
Also note that with a UICollectionView you should use the item property of IndexPath. Use row for UITableView. Though effectively they are the same.
Here's the updated method with all of the changes.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = myCollectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCollectionViewCell
if let imageSrc = liveMode[indexPath.item].image.first?.src {
cell.imageViewer.image = UIImage(named: imageSrc)
}
return cell
}

How to pass collectionview cells index value to nextviewcontroller(using push navigation) in swift

I am using collectionview in tableview cell,
i need to pass selected collectionview cells value to next viewcontroller, how?
code: here is the code for tableview and collectionview cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CategoryNewTableCell", for: indexPath) as! CategoryNewTableCell
let indexData = self.activeCategories?[indexPath.row]
cell.selectionStyle = .none
cell.catNameLbl.text = indexData?.details?.first?.title
cell.subCategories = indexData?.sub_categories
cell.clcSeller.reloadData()
}
class CategoryNewTableCell: UITableViewCell,UICollectionViewDelegate,UICollectionViewDataSource{
#IBOutlet weak var clcSeller: UICollectionView!
public var subCategories : Array<Sub_categories>?
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return subCategories?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SubCatCollectionCell", for: indexPath) as! SubCatCollectionCell
let subCategory = self.subCategories?[indexPath.item]
cell.lblTitle.text = langType == .en ? subCategory?.details?.first?.title : subCategory?.details?[1].title
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = StoryBoard.main.instantiateViewController(withIdentifier: "SearchResultVC") as! SearchResultVC
vc.subCatId = sub_categories?[indexPath.row].slug ?? ""
self.navigationController?.pushViewController(vc, animated: true)
}
}
here if i use didSelectItemAt for collectionview to send its selected cell value to next view controller
error:
Value of type 'CategoryNewTableCell' has no member 'navigationController'
if i give button action in main class then able to push but value is not going
class CategoryNewVC: UIViewController {
#IBAction func didselectcollectionviewBTn(_ sender: UIButton) {
let vc = StoryBoard.main.instantiateViewController(withIdentifier: "SearchResultVC") as! SearchResultVC
vc.subCatId = //here how to pass value
self.navigationController?.pushViewController(vc, animated: true)
}
}
here how to pass selected collectionview cells value to SearchResultVC please do help
EDIT
according to below answer i have added: still didSelectItemAt not called, why plz do help
class CategoryNewTableCell: UITableViewCell,UICollectionViewDelegate,UICollectionViewDataSource{
override func awakeFromNib() {
super.awakeFromNib()
self.clcSeller.delegate = self
self.clcSeller.dataSource = self
//
You can use protocol to send data to your nextviewcontroller.
protocol CategorySelectionDelegate {
func get(category: Sub_categories)
}
Declare the delegate in your CategoryNewTableCell and use it in didSelectItemAt method of your collectionviewcell like below:
class CategoryNewTableCell:
UITableViewCell,UICollectionViewDelegate,UICollectionViewDataSource{
#IBOutlet weak var clcSeller: UICollectionView!
public var subCategories : Array<Sub_categories>?
var delegate: CategorySelectionDelegate?
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return subCategories?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SubCatCollectionCell", for: indexPath) as! SubCatCollectionCell
let subCategory = self.subCategories?[indexPath.item]
cell.lblTitle.text = langType == .en ? subCategory?.details?.first?.title : subCategory?.details?[1].title
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate?.get(category: sub_categories?[indexPath.row])
}
}
Adopt the protocol in the receiving class
class CategoryNewVC: UIViewController, CategorySelectionDelegate {
func get(category: Sub_categories) {
let vc = StoryBoard.main.instantiateViewController(withIdentifier: "SearchResultVC") as! SearchResultVC
vc.subCatId = category.slug ?? ""
self.navigationController?.pushViewController(vc, animated: true)
}
}
Declare a callback in the tableViewCell subclass.
class CategoryNewTableCell: UITableViewCell {
var onSelectSubcategory: ((_ subcategoryID: String) -> Void)?
}
Assign this callback in your cellForRow like this.
cell.subCategories = indexData?.sub_categories
cell.onSelectSubcategory = { [weak self] (subcategoryID) in
let vc = StoryBoard.main.instantiateViewController(withIdentifier: "SearchResultVC") as! SearchResultVC
vc.subCatId = subcategoryID
self?.navigationController?.pushViewController(vc, animated: true)
}
Invoke this callback from collectionView didSelectItem like this.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let subcategoryID = sub_categories?[indexPath.item].slug ?? ""
self.onSelectSubcategory?(subcategoryID)
}

How to maintain state of collection view cell after dismissal of view?

So I have a collectionView that holds an array of trends users can click to feature that photo. When the cell is clicked, a checkmark appears letting the user know they have selected that category and their photoId is then entered into the selected childValues in the database.
Users have the option to edit their photo if they decided they want to remove their photo from a certain category. When I select edit profile, the cells that should be selected (ones I choose while uploading the photo) are unselected.
Or lets say for example, I already have a photo uploaded but now I want to feature it, when I go to edit photo and tap on a category the checkmark appears telling me this category is chosen. When I press save the photo is added to the chosen childValue in the database as expected, but when I click edit profile again and the view is presented. The cells I choose 10 seconds ago are now unselected.
How can I maintain a selected or deselected state even after dismissing the view controller?
var selectedCategoryValues = [String]()
var trend: Trend!
var selectedIndex = -1
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == trendCollectionView {
let trendsCell = collectionView.dequeueReusableCell(withReuseIdentifier:
"trendsCell", for: indexPath) as! TrendsCollectionCell
trendsCell.layer.cornerRadius = 9.0
trendsCell.layer.borderWidth = 0.1
trendsCell.layer.borderColor = UIColor.lightGray.cgColor
if selectedIndex == indexPath.row {
trendsCell.isSelected = true
} else {
trendsCell.isSelected = false
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView == trendCollectionView {
guard selectedIndex != indexPath.row else{return}
let indexpath = IndexPath(row:selectedIndex, section: 0)
trendCollectionView.cellForItem(at: indexpath)?.isSelected = !trendCollectionView.cellForItem(at: indexpath)!.isSelected
switch selectedSegmentIndex {
case 0: self.trend = femaleTrends[indexPath.row]
print("You selected \(trend.childValue)")
self.selectedCategoryValues.append(trend.childValue)
case 1: self.trend = maleTrends[indexPath.row]
print("You selected \(trend.childValue)")
self.selectedCategoryValues.append(trend.childValue)
default: break
}
Collection View Cell
class TrendsCollectionCell: UICollectionViewCell {
override var isSelected: Bool{
didSet{
if isSelected {
setSelectedUI()
}
else{
setUnSelectedUI()
}
}
}
func setSelectedUI(){
trendCheckmark.isHidden = false
trendCheckmark.tintColor = .white
}
func setUnSelectedUI(){
// reset to deafault, hide checkmark
trendCheckmark.isHidden = true
}
}
You can store the selected index as part of some state and then store / use that state as you wish. You'd do this by having some property selectedIndex, or, if you have more properties, you could have some struct viewState that you update in didSelectItemAt. You then either have some state manager outside of your view controllers (recommended) or you pass the state between view controllers (if you have a simple app).
Three needed steps for maintaining selected state of cell . This applicable for table view and collection view
1. Maintain last selected index for as selected cell .
2. Update cellindex on every did select call .
3. For every cellforrowindex method check if this selected cell or not.
Further explanation is on my previous answer
https://stackoverflow.com/questions/59293617/multiple-cells-selected-on-scrolling-reuse-cells-problem/
I never recommend using cell selected variable to detect cell selection because that variable is not completely describe the state , because of the dequeuing and recycling mechanism that work for UICollectionView and UITableView So I recommend Every time to rely all the state to your models ,
so you have 2 options
add an isSelcted property to every cell ModelObject
add Single property to your view Controller that reserve the selected Index
import Foundation
class NumberCountDataSource: NSObject , UICollectionViewDataSource {
let selectionAction: ((Int)->())
let numbersCount: Int
var selectedIndex: Int?
init(cardsCount: Int,selectionAction: #escaping ((Int)->())) {
self.numbersCount = cardsCount
self.selectionAction = selectionAction
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return numbersCount
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NumberCollectionViewCell.identiefier, for: indexPath) as! NumberCollectionViewCell
cell.configure(number: indexPath.row + 1, isSelected: indexPath.row == selectedIndex)
return cell
}
}
extension NumberCountDataSource: UICollectionViewDelegate , UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedIndex = indexPath.row
self.selectionAction(indexPath.row)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return CGFloat.init(0.0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return CGFloat.init(0.0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize.init(width: 60, height: 50)
}
}
class NumberCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var numberLable: UILabel!
#IBOutlet weak var backGroundview: AnimatableView!
static let identiefier: String = "NumberCollectionViewCell"
static let nibName: String = "NumberCollectionViewCell"
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override var isSelected: Bool {
didSet {
setupSelection(iseSelected: isSelected)
}
}
private func setupSelection(iseSelected: Bool) {
backGroundview.backgroundColor = isSelected ? UIColor("#4d8ec5") : UIColor("#ffffff")
backGroundview.borderColor = isSelected ? UIColor.clear : UIColor("#707070")
numberLable.textColor = isSelected ? UIColor("#ffffff") : UIColor("#444e53")
}
func configure(number: Int,isSelected: Bool) {
numberLable.text = "\(number)"
setupSelection(iseSelected: isSelected)
}
}

UICollectionView cellForItem(at:) crashes

I am using Swift4 and Xcode9.
I create my collectionView cells by using the delegate method below;
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mediaItemCell", for: indexPath) as! MediaItemCollectionViewCell
cell.mediaItemCellImageView.image = mediaPlayer?.item(at: indexPath.row)?.image
return cell
}
Then, I want to replace the image of selected cell with a "stop" image. To achieve this, I have tried to use didSelectItemAt method;
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if (mediaPlayer?.isPlaying == true) {
// stop the player
mediaPlayer?.stopPlayingMedia()
} else {
// change the image and start playing
let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell // CRASH
cell.mediaItemCellImageView.image = UIImage(named: "stop.png")
...
...
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
// restore the original image
let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell
cell.mediaItemCellImageView.image = mediaPlayer?.item(at: indexPath.row)?.image
// stop playing
mediaPlayer?.stopPlayingMedia()
}
Now, when I select the cell which is already selected (hence the original image has already been replaced with "stop.png") the code above works; i.e. the original image is restored, which is done by stopPlayingMedia() method.
When I select a cell different than the currently selected one, the App crashes. I tried to move the didSelectItemAt call out of the delegate method, but it did not work.
Surprisingly, the logical check if collectionView.cellForItem(at: indexPath) is MediaItemCollectionViewCell succeeds when I select the currently selected cell, and fails when I select another cell.
I want to change the image of the cell, hence need to cast the variable type but it fails. Why does to cast fail when I select a different cell than the currently selected one?
Thanks
Edit:
I use collectionView.reloadData() to restore all the images to originals (I mentioned this in the OP - when stopPlayingMedia() is invoked). It appears that, if I add a private var index:IndexItem? to my ViewController class and update its value within didSelectItemAt (to save the last selected cell) and use the code below, the code does not crash
extension ViewController:MediaPlayerControllerDelegate {
// this delegate method is invoked when stopPlayingMedia() is called
func mediaPlayerControllerDidFinishPlaying() {
// restore the UI
// collectionView.reloadData() -> this causes the crash, instead, I only restore one cell
let cell = collectionView.cellForItem(at: index!) as! MediaItemCollectionViewCell
cell.mediaItemCellImageView.image = mediaPlayer?.item(at: (index?.row)!)?.image
timeLabel.text = "00:00"
progressBar.progress = 0.0
}
}
I tried the following to create cell and changing the image inside cell on selection. Not crashing at all. Please check the implementation, It might help you.
import UIKit
class ViewController: UIViewController {
private var isPlayingModel = [false, false, false, false, false] // initially all are stop, not playing
#IBOutlet weak var collectionView: UICollectionView! {
didSet{
collectionView.delegate = self
collectionView.dataSource = self
}
}
}
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return isPlayingModel.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mediaItemCell", for: indexPath) as! MediaItemCollectionViewCell
cell.imageVw.image = isPlayingModel[indexPath.row] ? #imageLiteral(resourceName: "start") : #imageLiteral(resourceName: "stop")
return cell
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
isPlayingModel[indexPath.row] = !isPlayingModel[indexPath.row]
let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell
cell.imageVw.image = isPlayingModel[indexPath.row] ? #imageLiteral(resourceName: "start") : #imageLiteral(resourceName: "stop")
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.size.width, height: 60)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 4
}
}
class MediaItemCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imageVw: UIImageView!
}
Check the implementation here.
You are not passing indexPath correctly to load a cell, that's why it is crashing.Edit your didSelectItemAt: method as follows:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedCell = IndexPath(item: indexPath.item, section: indexPath.section)
let cell = collectionView.cellForItem(at: selectedCell) as! ACollectionViewCell
cell.imgView.image = UIImage(named: "3.jpg")
}
Collection view indexPath has element item, and not row
Try this and see: (let me know what does it print)
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
// restore the original image
if let cell = collectionView.cellForItem(at: indexPath) as? MediaItemCollectionViewCell {
if let cellImage = mediaPlayer?.item(at: indexPath.item)?.image { // Collection view indexPath has element `item`, and not `row`
cell.mediaItemCellImageView.image = cellImage
} else {
print("cellImage not found at indexPath - \(indexPath)")
}
} else {
print("collection view cell `MediaItemCollectionViewCell` not found at indexPath -\(indexPath) ")
}
// stop playing
mediaPlayer?.stopPlayingMedia()
}

How to show check tick mark in Collection view (images)

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.
}
}
}

Resources