Refreshing or reloading data on just single object inside a collectionViewCell - swift

i'm trying to update just one single object inside my costumViewCell,
i've tried collectionView.reloadItems(at: [IndexPath]), but this method updates my entire cell, which results to a very jittering animations.
here is a sample code of my collectionView cell,
class MyCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var buttonA: UIButton!
#IBOutlet weak var buttonB: UIButton!
var myButtonTitle: String? {
didSet{
if let title = myButtonTitle {
self.buttonA.setTitle(title, for: .normal)
}
}
}
var buttonActionCallBack: (()->()?)
override func awakeFromNib() {
super.awakeFromNib()
self.animation()
buttonA.addTarget(self, action: #selector(buttonACallBack), for: .touchUpInside)
}
#objc fileprivate func buttonACallBack() {
self.buttonActionCallBack?()
}
fileprivate func animation() {
UIView.animate(withDuration: 1.0) {
self.buttonA.transform = CGAffineTransform(translationX: 20, y: 20)
self.buttonB.transform = CGAffineTransform(translationX: 20, y: 20)
}
}
}
here is my DataSource method.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! MyCollectionViewCell
let item = mainList[indexPath.row]
collectionView.reloadItems(at: <#T##[IndexPath]#>)
cell.buttonActionCallBack = {
() in
//Do Stuff and Update Just ButtonA Title
}
return cell
}
cheers.

The jittering animation occurs because of this collectionView.reloadItems(at: [IndexPath]) line written inside cellForItemAt which is really wrong approach because cellForItemAt called many a times leads to infinite loop of reloading IndexPath's. Instead of that, you just reload only that part which is necessary when action occurs.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! MyCollectionViewCell
let item = mainList[indexPath.row]
//collectionView.reloadItems(at: <#T##[IndexPath]#>) #removed
cell.buttonActionCallBack = {
() in
//Do Stuff and Update Just ButtonA Title
collectionView.reloadItems(at: [indexPath]) //Update after the change occurs to see the new UI updates
}
return cell
}

Related

Receiving an error of when trying to add text to a label in a collection view

I am trying to input the arrayOfValues[indexPath.item] as the text for my textLabel in the collection view, but receive an error when I run the program saying 'Fatal error: Index Out of Range'
How would I fix this so that the collectionView cells are populated with information from the arrayOfValues?
Here is the code.
import UIKit
class NetworkViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var firstCollectionView: UICollectionView!
#IBOutlet weak var secondCollectionView: UICollectionView!
let arrayOfOrganizations = ["My Network", "Find Connections", "ss"]
let arrayOfValues = [""]
override func viewDidLoad() {
super.viewDidLoad()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if (collectionView == secondCollectionView) {
return arrayOfOrganizations.count
}
return arrayOfValues.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let firstCell = firstCollectionView.dequeueReusableCell(withReuseIdentifier: "firstCell", for: indexPath) as! FirstCollectionViewCell
firstCell.textLabel.text = arrayOfValues[indexPath.item] //error on this line
if (collectionView == secondCollectionView) {
let secondCell = secondCollectionView.dequeueReusableCell(withReuseIdentifier: "secondCell", for: indexPath) as! SecondCollectionViewCell
secondCell.backgroundColor = .black
return secondCell
}
return firstCell
}
}
class FirstCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var textLabel: UILabel!
}
class SecondCollectionViewCell: UICollectionViewCell {
}
The problem you had was the the first two lines of your cellForItemAt function were getting executed no matter the collectionView. So, essentially, you need to make sure the block of code corresponding to the firstCollectionView gets executed only when collectionView == firstCollectionView, and same goes for the secondCollectionView. In short, you just need to change your function to this instead:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if (collectionView == secondCollectionView) {
let secondCell = secondCollectionView.dequeueReusableCell(withReuseIdentifier: "secondCell", for: indexPath) as! SecondCollectionViewCell
secondCell.backgroundColor = .black
return secondCell
} else {
let firstCell = firstCollectionView.dequeueReusableCell(withReuseIdentifier: "firstCell", for: indexPath) as! FirstCollectionViewCell
firstCell.textLabel.text = arrayOfValues[indexPath.item] //error on this line
return firstCell
}
}

How to get the TextField's text located inside a collectionviewcell?

Im kind of new with Swift, what I am trying to do is to get the text typed by an user in a TextField which is located inside a collection view cell. I have a CollectionViewCell named "PestañaCero" where I created the TextField, this one:
import Foundation
import UIKit
class PestañaCero: UICollectionViewCell
{
let NombreUsuarioTextField: UITextField =
{
let nombre = UITextField()
nombre.borderStyle = UITextBorderStyle.roundedRect
nombre.placeholder = "Nombre de Usuario"
nombre.textAlignment = .center
return nombre
}()
let NumerodeContactoTextField: UITextField =
{
let nombre = UITextField()
nombre.borderStyle = UITextBorderStyle.roundedRect
nombre.placeholder = "Numero de Contacto"
nombre.textAlignment = .center
return nombre
}()
let DireccionOrigenTextField: UITextField =
{
let nombre = UITextField()
nombre.borderStyle = UITextBorderStyle.roundedRect
nombre.placeholder = "Direccion de Origen"
nombre.textAlignment = .center
return nombre
}()
let DireccionDestinoTextField: UITextField =
{
let nombre = UITextField()
nombre.borderStyle = UITextBorderStyle.roundedRect
nombre.placeholder = "Direccion de Destino"
nombre.textAlignment = .center
return nombre
}()
func setupViews()
{
addSubview(NombreUsuarioTextField)
addSubview(NumerodeContactoTextField)
addSubview(DireccionOrigenTextField)
addSubview(DireccionDestinoTextField)
//VERTICAL CONSTRAINT
addConstraintsWithFormat("H:|-16-[v0]-16-|", views: NombreUsuarioTextField)
addConstraintsWithFormat("H:|-16-[v0]-16-|", views: NumerodeContactoTextField)
addConstraintsWithFormat("H:|-16-[v0]-16-|", views: DireccionOrigenTextField)
addConstraintsWithFormat("H:|-16-[v0]-16-|", views: DireccionDestinoTextField)
addConstraintsWithFormat("V:|-100-[v0(30)]-12-[v1(30)]-12-[v2(30)]-12-[v3(30)]", views:
NombreUsuarioTextField,NumerodeContactoTextField, DireccionOrigenTextField ,DireccionDestinoTextField)
}
}
Im trying to print the text when touching in a button created in my cellForItemAt, code which is located in my UICollectionViewController class
#objc func confirmarbutton()
{
print("123")
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
var myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "PestañaCero", for: indexPath)
myCell.backgroundColor = UIColor.black
let nombre = UIButton(frame: CGRect(x: myCell.frame.width/2-100, y: 400, width: 200, height: 25))
nombre.setTitle("Pedir Domicilio", for: .normal)
nombre.backgroundColor = UIColor.orange
nombre.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
nombre.addTarget(self, action: #selector(confirmarbutton) , for: .touchUpInside)
myCell.addSubview(nombre)
}
Any help would be really appreciated, thanks everyone
You can set the delegate of the textField inside the cell to controller when the cell is created, cell.NumerodeContactoTextField.delegate = self and then use the delegate in the controller. However, the problem with this approach is that you will have to do it for all textFields, so the better solution would be create your own delegate, in cell, like this:
protocol CollectionCellTextFieldDelegate: class {
func textDidChanged(_ textField: UITextField)
}
And then add this to your cell:
class PestañaCero: UICollectionViewCell {
weak var textFieldDelegate: CollectionCellTextFieldDelegate?
}
Now in your cell creation in the controller you do:
cell.textFieldDelegate = self
Conform and implement the delegate in the controller:
func textDidChanged(_ textField: UITextField) {
//Here you will get the textField, and you can extract the textFields text
}
This is just an example of how you would approach this situation. you should be able to modify based on your requirement.
A Small Sample of how You would go about doing this with above approach
My Cell Class
import UIKit
protocol CollectionCellTextFieldDelegate: class {
func cellTextFields(_ fields: [UITextField])
}
class Cell: UICollectionViewCell {
#IBOutlet weak var fieldOne: UITextField!
#IBOutlet weak var fieldTwo: UITextField!
#IBOutlet weak var button: UIButton!
weak var textFieldDelegate: CollectionCellTextFieldDelegate?
#IBAction func buttonClicked(_ sender: UIButton) {
guard let textFieldDelegate = textFieldDelegate else { return } //we don't have do anything if not conformed to delegate
//otherwise pass all textFields
textFieldDelegate.cellTextFields([fieldOne, fieldTwo])
}
}
My Controller Class
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, CollectionCellTextFieldDelegate {
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
//register the cell xib
collectionView.register(UINib(nibName: "Cell", bundle: nil), forCellWithReuseIdentifier: "Cell")
}
//MARK:- CollectionView
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
cell.textFieldDelegate = self
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.bounds.width - 20.0, height: 175.0)
}
//you could write this delegate anyway you want, its just for a sample
func cellTextFields(_ fields: [UITextField]) {
//loop over each fields and get the text value
fields.forEach {
debugPrint($0.text ?? "Empty Field")
}
}
}
You will probably have to handle dequeueing of cells as well but for now test this code and modify accordingly.
#objc func confirmarbutton(sender:UIButton)
{
let indexPath = self.collView.indexPathForItem(at: sender.convert(CGPoint.zero, to: self.collView))
let cell = self.collView.cellForItem(at: indexPath!) as! PestañaCero
print(cell.NombreUsuarioTextField.text) // use textfield value like this
print("123")
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
var myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "PestañaCero", for: indexPath)
myCell.backgroundColor = UIColor.black
let nombre = UIButton(frame: CGRect(x: myCell.frame.width/2-100, y: 400, width: 200, height: 25))
nombre.setTitle("Pedir Domicilio", for: .normal)
nombre.backgroundColor = UIColor.orange
nombre.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
nombre.addTarget(self, action: #selector(confirmarbutton(sender:)) , for: .touchUpInside)
myCell.addSubview(nombre)
}
you can access Any row using the indexpath you just need to pass specific indexpath in cellForItem method to get that row so i just pass my sender and find that row to get that textfield value you just replace my code with yours and it will work :)
Here is a simple solution which I personally follow :
First we have should be able to figure it out that at which index/row's button user has clicked so to know that we will set the "indexPath" to button layer like below in * cellForItemAt* method:
nombre.layer.setValue(indexPath, forKey: "indexPath")
then we need change signature of confirmarbutton method like below (as written in answer by #Mahesh Dangar):
#objc func confirmarbutton(sender:UIButton)
Then we need the indexPath in confirmarbutton method so we can get the cell First and then text field to access the value of that text field :
#objc func confirmarbutton(sender:UIButton){
let indexPath = sender.layer.value(forKey: "indexPath") as! IndexPath
let cell = collectionView.cellForItem(at: indexPath) as! PestañaCero
let number = cell.NombreUsuarioTextField.text! // make sure you have value in textfield else you will get runTime error
//below is safer alternative to above line...write one of them
if let isNumberEntered = cell.NombreUsuarioTextField.text{
//condition will be true if text field contains value
}else{
//This block will be executed if text field does not contain value/it is empty. you can show alert something like please enter the number etc.
}
}

UICollectionview - blink when move item

I want to reorder my cells in my UICollectionView. But when I drop my item, the "cellForItemAt" method is called and this will cause the cell to flash (See image below).
What should I do to avoid this behavior ?
Thank you in advance for your help.
class ViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
private let cellIdentifier = "cell"
private let cells = [""]
private var longPressGesture: UILongPressGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.handleLongGesture(gesture:)))
collectionView.addGestureRecognizer(longPressGesture)
}
//Selectors
#objc func handleLongGesture(gesture: UILongPressGestureRecognizer) {
switch(gesture.state) {
case .began:
guard let selectedIndexPath = collectionView.indexPathForItem(at: gesture.location(in: collectionView)) else {
break
}
collectionView.beginInteractiveMovementForItem(at: selectedIndexPath)
case .changed:
collectionView.updateInteractiveMovementTargetPosition(gesture.location(in: gesture.view!))
case .ended:
collectionView.endInteractiveMovement()
default:
collectionView.cancelInteractiveMovement()
}
}
}
// MARK: - UICollectionViewDataSource
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {
return true
}
func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
}
}
// MARK: - UICollectionViewDelegateFlowLayout
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height: 100)
}
}
You need to call endInteractiveMovement in perfomBatchUpdates.
But whenever endInteractiveMovement triggered, cellForRow called. So cell will be refreshed and new cell will added(check with random color extension). To secure that, you need to save selectedCell in variable. And return that cell when endInteractiveMovement called.
Declare currentCell in ViewController
var isEnded: Bool = true
var currentCell: UICollectionViewCell? = nil
Store selected cell in variable when gesture began & call endInteractiveMovement in performBatchUpdates.
So, your handleLongGesture func look like below:
//Selectors
#objc func handleLongGesture(gesture: UILongPressGestureRecognizer) {
switch(gesture.state) {
case .began:
guard let selectedIndexPath = collectionView.indexPathForItem(at: gesture.location(in: collectionView)) else {
break
}
isEnded = false
//store selected cell in currentCell variable
currentCell = collectionView.cellForItem(at: selectedIndexPath)
collectionView.beginInteractiveMovementForItem(at: selectedIndexPath)
case .changed:
collectionView.updateInteractiveMovementTargetPosition(gesture.location(in: gesture.view!))
case .ended:
isEnded = true
collectionView.performBatchUpdates({
self.collectionView.endInteractiveMovement()
}) { (result) in
self.currentCell = nil
}
default:
isEnded = true
collectionView.cancelInteractiveMovement()
}
}
Also need to change cellForRow
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if currentCell != nil && isEnded {
return currentCell!
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath)
cell.backgroundColor = .random
return cell
}
}
TIP
Use random color extension for better testing
extension UIColor {
public class var random: UIColor {
return UIColor(red: CGFloat(drand48()), green: CGFloat(drand48()), blue: CGFloat(drand48()), alpha: 1.0)
}
}
EDIT
If you have multiple sections.
Lets take array of array
var data: [[String]] = [["1","2"],
["1","2","3","4","5","6","7"],
["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15"]]
Then you need to maintain data when reordering
func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
print("\(sourceIndexPath) -> \(destinationIndexPath)")
let movedItem = data[sourceIndexPath.section][sourceIndexPath.item]
data[sourceIndexPath.section].remove(at: sourceIndexPath.item)
data[destinationIndexPath.section].insert(movedItem, at: destinationIndexPath.item)
}
You can try to call
collectionView.reloadItems(at: [sourceIndexPath, destinationIndexPath])
right after all your updates (drag and drop animation) are done.
For example call it after performBatchUpdates. It will remove blinking.

Tap button to change UICollectionView Cell size?

I have a collection view on my HomeController (UICollectionView) Class and my PostCell (UICollectionViewCell) class. HomeController has just 4 different cells. I also have a button inside my PostCell that when touched, handles a handleChangeSize function. When I touch that button, I want that specific cells size to change. Preferably change in height, similar to Instagrams "show more" feature on their cells. Any help will be highly, highly appreciated. Thank you... Here's my code:
class HomeController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = UIColor(white: 0.90, alpha: 1.0)
collectionView?.register(PostCell.self, forCellWithReuseIdentifier: cellId)
collectionView?.showsVerticalScrollIndicator = false
collectionView?.alwaysBounceVertical = true
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! PostCell
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let height = (view.frame.width) * 9 / 16
return CGSize(width: view.frame.width, height: height + 50 + 50)
}
}
class PostCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
lazy var myButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Change Size", for: .normal)
button.addTarget(self, action: #selector(handleChangeSize), for: .touchUpInside)
return button
}()
func handleChangeSize() {
}
func setupViews() {
addSubview(myButton)
myButton.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
myButton.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I have done this in tableView. follow the same for collectionView.
var expandedCells = [Int]()
here is the cellForRowAtIndexPath method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "RequestTableViewCell") as! RequestTableViewCell
cell.selectionStyle = .none
cell.optionButton.tag = indexPath.row
cell.optionButton?.addTarget(self, action: #selector(RequestsViewController.deleteAction), for: UIControlEvents.touchUpInside)
return cell
}
and the action for option button in tableViewCell is
func deleteAction(_ sender: UIButton){
if let button = sender as? UIButton {
// If the array contains the button that was pressed, then remove that button from the array
if expandedCells.contains(sender.tag) {
expandedCells = expandedCells.filter { $0 != sender.tag }
}
// Otherwise, add the button to the array
else {
expandedCells.removeAll()
expandedCells.append(sender.tag)
}
// Reload the tableView data anytime a button is pressed
self.requestTableView.beginUpdates()
self.requestTableView.endUpdates()
}
}
and the heightForRowAtIndexPath method is
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// Set the row height based on whether or not the Int associated with that row is contained in the expandedCells array
if expandedCells.contains(indexPath.row) {
return 112
} else {
return 67
}
}
The solution to this problem is quite easy. First, you need to specify what height you want to set for the particular cell and perform that change in handleChangeSize() method (this could be done by simple frame.size.height = ...).
After that, you probably need to resize the whole collection view, otherwise, there are going to be some nasty surprises for you. Perform the calculation (to get the new height) after you have resized the cell (you might call a notification or something that triggers whenever a to resize is necessary).

Toggle image in UICollectionViewCell when cell selected

I have a UICollectionView of items, and I would like an image in a cell to be toggled when the user selects the cell.
I have a custom UICollectionViewCell:
class RDCell: UICollectionViewCell {
var textLabel: UILabel!
var imageView: UIImageView!
var isSelected: Bool!
...(do init and all that good stuff)
}
And selctected item in collection view :
func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
let celld = (collectionView.dequeueReusableCellWithReuseIdentifier("collectionCell", forIndexPath: indexPath) as? RDCell)!
let indexPaths = collectionView.indexPathsForSelectedItems()
let newCell = collectionView.cellForItemAtIndexPath(indexPath) as! RDCell
if(celld.selected){
var image: UIImage = UIImage(named: "notSelected")!
newCell.imageView.image = image
newCell.selected = false
}else{
var image: UIImage = UIImage(named: "selected")!
newCell.imageView.image = image
newCell.selected = true
}
return true
}
My attempt partially works, after selecting and unselecting one item I am not able to select it again after, I need to select a different cell before returning to select the first, and this same bug happens on all selected cells.
Any suggestions or another way to implement the functionality I seek would be greatly appreciated.
Swift 4
I just use the property "highlightedImage" in the "cellForItemAt indexPath" method of the collectionView and set another image on it.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.backgroundColor = UIColor.clear
let imageView = cell.viewWithTag(1) as! UIImageView
imageView.image = imagesArray1[indexPath.row]
imageView.highlightedImage = imagesArray2[indexPath.row]
imageView.contentMode = .scaleAspectFill
return cell
}
In my case, i assigned a tag in the imageView and call it through it.
Best regards.
You should never call cellForItemAtIndexPath directly. You have no guarantee of what cell you're getting and the changes you make may have no effect.
The proper way to do this is to track your state within the class and change the state of the cell in cellForItemAtIndexPath. Then you simply call collectionView?.reloadData() when you need to update the views.
Simple example for reference:
var selectionTracking: [[Bool]] = []
func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
if let selected = selectionTracking[indexPath.section][indexPath.row] {
return selected
}
else{
selectionTracking[indexPath.section][indexPath.row] = false
}
return true
}
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
if let selected = selectionTracking[indexPath.section][indexPath.row] {
selectionTracking[indexPath.section][indexPath.row] = !selectionTracking[indexPath.section][indexPath.row]
}
else{
selectionTracking[indexPath.section][indexPath.row] = true
}
collectionView?.reloadData()
return true
}
My approach will be different for this, i would have used the didDeselectItemAt method of the collectionView...
Swift 3:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let newCell = collectionView.cellForItem(at: indexPath)
newCell.imageView.image = imageAfterSelection[indexPath.row]
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let newCell = collectionView.cellForItem(at: indexPath)
newCell.imageView.image = imagesAfterDeselection[indexPath.row]
}
Swift 4/5:
Inside your collectionViewCell class define a variable called imageName
then override isSelected property to set the image name based on selected state and in collectionView cellForItemAt method set the value for imageName variable for each cell.
CollectionViewCell Class:
class YourCollectionViewCell: UICollectionViewCell
{
#IBOutlet var cellIcon: UIImageView!
var imageName = ""
override var isSelected: Bool {
didSet {
if self.isSelected {
self.cellIcon.image = UIImage(named: "\(self.imageName) Highlighted")
} else {
self.cellIcon.image = UIImage(named: "\(self.imageName) Unhighlighted")
}
}
}
}
CollectionView DataSource:
class YourCollectionVewController: UICollectionViewDataSource
{
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// this line is an extension don't copy paste
let cell = collectionView.dequeueReusableCell(with: YourCollectionViewCell.self, for: indexPath)
let imageName = "yourImageName Unhighlighted"
cell.cellIcon.image = UIImage(named: imageName)
cell.imageName = imageName
return cell
}
}