Swift 4 - deselect collection view cell if already selected - ios

I have a collection view where users can select multiple cells and send the array of index to a server for saving their selection.
everything is working except that when the collection view is created the selected items needs to be clicked two times in order to deselect them.
How can I solve this issue of double clicking ?
extension ThirdViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return categoryList.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! UserCategoryCollectionViewCell
cell.categoryLbl.text = categoryList[indexPath.row]
if Defaults.hasKey(.categoryListIndices) {
if (Defaults[.categoryListIndices]?.contains(indexPath.row))! {
cell.alpha = 0.1
cell.isSelected = true
} else {
cell.alpha = 1
}
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! UserCategoryCollectionViewCell
cell.alpha = 0.1
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let deselectedCell = collectionView.cellForItem(at: indexPath) as? UserCategoryCollectionViewCell
deselectedCell?.alpha = 1
print(deselectedCell?.isSelected)
collectionView.deselectItem(at: indexPath, animated: false)
}
}

first of all. add this to your viewDidLoad.
collectionView?.allowsMultipleSelection = true
and then add these two functions.
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell=collectionView.cellForItem(at: indexPath)
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: [])
cell?.backgroundColor = Color.hexStringToUIColor(hex: "#F5A331")
let lbl = cell?.subviews[1] as! UILabel
lbl.textColor = Style.cellHighlightedColor
}
override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
collectionView.deselectItem(at: indexPath, animated: true)
let cell=collectionView.cellForItem(at: indexPath)
collectionView.deselectItem(at: indexPath, animated: true)
cell?.backgroundColor = UIColor.white
let lbl = cell?.subviews[1] as! UILabel
lbl.textColor = UIColor.darkGray
}
This will do

Related

Deselect default selected cell on selecting any other cell in UICollectionview

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

Why after selecting cell programmatically - cell is not interaction?

I set a cell in the collectionView selected programmatically :
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(with: FAFavouriteCategoryCell.self, for: indexPath)!
let currentCategory = categories[indexPath.row]
cell.isSelected = selectedCategories.contains(currentCategory.categoryID)
cell.configCell(category: currentCategory)
return cell
}
after this all selected cells not working. I can't click on It.
If I clicked method shouldDeselectItemAt not calling
You need to call selectItem like this
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(with: FAFavouriteCategoryCell.self, for: indexPath)!
let currentCategory = categories[indexPath.row]
if selectedCategories.contains(currentCategory.categoryID) {
collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .left) //Add this line
cell.isSelected = true
} else {
collectionView.deselectItem(at: indexPath, animated: false)
cell.isSelected = false
}
cell.configCell(category: currentCategory)
return cell
}

How to automatically scrolling collectionviewcell to nearest cell(previous or next) when selecting a cell

I'm currently working on a swift application. In one view controller I have a collectionView with horizontal scrolling. The collectionView is look like horizontal tabs with more tabs. So some collectionViewCell's are not visible at initial time.
What I need is I want to auto-scroll the collectionView to next cell(if collectionView has an invisible cell in the right) or to the previous cell(if it has an invisible cell in the left) when I select a collectionViewCell. I was using a cocoa pod(SwipeMenuViewController), but it is making some issue with the presentation. Please help me to implement the same with collectionView.
Please see the image for more clarity.
This is my code,
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.tabArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "tabCell", for: indexPath) as! tabCollectionViewCell
cell.tabLabel.text = self.tabArray[indexPath.row]
if selectedTabArray.contains(indexPath) {
cell.bottomView.isHidden = false
} else {
cell.bottomView.isHidden = true
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.selectedTabArray.removeAll()
selectedTabArray.append(indexPath)
self.tabCollectionView.reloadData()
tabCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
You can just scroll to that indexPath on didSelectItemAt Method
var selectedIndex = 0
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "tabCell", for: indexPath) as! tabCollectionViewCell
cell.tabLabel.text = self.tabArray[indexPath.item]
if self.selectedIndex == indexpath.item {
cell.bottomView.isHidden = false
} else {
cell.bottomView.isHidden = true
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.selectedIndex = indexpath.item
self.tabCollectionView.reloadData()
tabCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
Hope this Help!
You can use the didSelectRowAt method to get the indexPath of the selected cell. then check if the cell is not the last cell. If it is not, scroll to right, else scroll to left.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
collectionView.reloadItems(at: [indexPath])
}
UPDATE1:
I didn't see the image when answering the question as it was not added yet. I have update my answer.
UPDATE2:
In your viewDidLoad,
collectionView.allowsMultipleSelection = false // in your view didLoad
then use property observers to change the selected index.
var selectedIndexPath : IndexPath = IndexPath(item: 0, section: 0) {
didSet {
self.collectionView.reloadItems(at: [selectedIndexPath])
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.tabArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "tabCell", for: indexPath) as! tabCollectionViewCell
cell.tabLabel.text = self.tabArray[indexPath.row]
cell.bottomView.isHidden = (indexPath == selectedIndexPath) ? false : true
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedIndexPath = indexPath
tabCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! tabCollectionViewCell
cell.bottomView.isHidden = true
}
var selectedIndex = 0
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "tabCell", for: indexPath) as! tabCollectionViewCell
cell.tabLabel.text = self.tabArray[indexPath.item]
if self.selectedIndex == indexpath.item {
cell.bottomView.isHidden = false
} else {
cell.bottomView.isHidden = true
}
return cell
}
Replace collectionView didSelectItemAt with following method to solve selection issue and reload collectionview,
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.selectedIndex = indexpath.item
UIView.animate(withDuration: 0, animations: {
tabCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}) { (_) in
self.tabCollectionView.reloadData()
}
}

Swift make select/unselect in single/multiple selection mode for UICollectionView inside UITableViewCell

I am new to iOS and I want to implement a UICollectionView inside a UITableView which can have multiple select/unselect in section 1 of the UITableview. And in section 2, only a single selection is allowed. And save the selection sate event if I dismiss the Viewcontroller and when I open it again, it must show the last selected cell as highlight.
I searched for tutorials but all of them don't mention to select/unselect the state of collection cell or saving state after dismissing view controller.
Can someone help to implement it?
Thank in advance!
Here is my code I do till now:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return clvData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "clvCell", for: indexPath) as! demoCollectionViewCell
cell.title.text = clvData[indexPath.item] as? String
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = .red
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = .white
}
you guys can also check my project here:
https://mega.nz/#!xRs0EQyQ
try this and let me know if you have any problem or if this solved your problem.
var arrSelectedIndex:[IndexPath] = []// store this array either in database by api or in local
var clvData:[String] = []// your data array
//get the arrSelectedIndex from default in viewDidLoad before reloading the table and collection.
override func viewDidLoad() {
super.viewDidLoad()
if let myArray = UserDefaults.standard.array(forKey: "selectedArray") as? [IndexPath] {
arrSelectedIndex = myArray
} else {
arrSelectedIndex = []
}
}
// and save the arrSelectedIndex in viewWillDisappear method
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
UserDefaults.standard.set(arrSelectedIndex, forKey: "selectedArray")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return clvData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "clvCell", for: indexPath) as! demoCollectionViewCell
cell.title.text = clvData[indexPath.item] as? String
if arrSelectedIndex.contains(indexPath) { // You need to check wether selected index array contain current index if yes then change the color
cell.backgroundColor = UIColor.red
}
else {
cell.backgroundColor = UIColor.white
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = .red
if !arrSelectedIndex.contains(indexPath) {// if it does not contains the index then add it
arrSelectedIndex.append(indexPath)
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.backgroundColor = .white
if let currIndex = arrSelectedIndex.firstIndex(of: indexPath) {// if it contains the index then delete from array
arrSelectedIndex.remove(at: currIndex)
}
}

multiple cells selected on scrolling [reuse cells problem]

I have a collectionViewCell which contains an image and a label. When I select a cell and scroll ahead I find other cell being selected. Also when I scroll back I find the other cell and not the cell I selected is selected.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return followedUsers.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = attachProfilesCollectionView.dequeueReusableCell(withReuseIdentifier: "attachCells", for: indexPath) as? attachUsersCell
cell!.subscribedUserId = self.followedUsers[indexPath.row].userId
cell?.profileNameToAttah.text = self.followedUsers[indexPath.row].fullName
cell?.profileImageToAttch.loadImagesWithUrl(from: self.followedUsers[indexPath.row].ImagePath)
if mLastSelectedIndex == indexPath.row {
cell?.isSelected = true}
else{cell?.isSelected = false}
return cell!
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 80, height: 110)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = attachProfilesCollectionView.cellForItem(at: indexPath) as! attachUsersCell
cell.subscribedUserId = following[indexPath.item]
usersToAttach.append(cell.subscribedUserId)
attachedCounter += 1
attachCounterFun()
print(usersToAttach)
cell.checkMarkImage.isHidden = false
attachCounterFun()
guard mLastSelectedIndex != indexPath.row else{return}
mLastSelectedIndex = indexPath.row
print("this is addition \(usersToAttach)")
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell_ = attachProfilesCollectionView.cellForItem(at: indexPath) as! attachUsersCell
let toRemove = usersToAttach.index(of: following[indexPath.row])
usersToAttach.remove(at: toRemove!)
attachedCounter -= 1
attachCounterFun()
print(usersToAttach)
cell_.checkMarkImage.isHidden = true
print("this is removal \(usersToAttach)")
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if isSelected {
cell.isSelected = true}
else { isSelected = false }
}
At present I have enabled paging and almost there but occasionally the selected cells change selection upon scroll
Maintain global variable in your controller
var mLastSelectedIndex = -1
Whenever cellForRowAtIndexPath method called check if current cell is selected or not .If this is selected One update its UI. I suggest you to change selected cell UI on cell class only. This way you can easily manage cell tapping and helps you writing clean code.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = attachProfilesCollectionView.dequeueReusableCell(withReuseIdentifier: "attachCells", for: indexPath) as? attachUsersCell
cell!.subscribedUserId = self.followedUsers[indexPath.row].userId
cell?.profileNameToAttah.text = self.followedUsers[indexPath.row].fullName
cell?.profileImageToAttch.loadImagesWithUrl(from: self.followedUsers[indexPath.row].ImagePath)
// here update selected index path
if mLastSelectedIndex == indexPath.row {
cell?.isSelected = true
}
else{
cell?.isSelected = false
}
return cell!
}
Update last selected index on cell tapped.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard mLastSelectedIndex != IndexPath.row else{return}
let indexpath = IndexPath(row:mLastSelectedIndex, section: 0)
mColllectionView.cellForItem(at: indexpath)?.isSelected = !mColllectionView.cellForItem(at: indexpath)!.isSelected
mLastSelectedIndex = IndexPath.row
}
In your attachUsersCell.swift declare isSelected property of cell . The default value of this property is false, which indicates that the cell is not selected.
override var isSelected: Bool{
didSet{
if isSelected {
setSelectedUI()
}
else{
setUnSelectedUI()
}
}
}
func setSelectedUI(){
borderColor = UIColor.systemOrange
borderWidth = 2
cornerRadius = 8
clipsToBounds = true
}
func setUnSelectedUI(){
// reset to default ui
}
Hope this helps!

Resources