How can I select specific indexpath of a UICollectionView by default? - ios

I want to highlight the specific cell of the UICollectionView by default.
However I can not do that.The code is below.
UIView(in the init method):
let selectedIndexPath = IndexPath(item: 0, section: 0)
collectionView.selectItem(at: selectedIndexPath, animated: false, scrollPosition: [])
UICollectionViewCell:
override var isSelected: Bool {
didSet {
imageView.tintColor = isSelected ? UIColor.white : UIColor.red
}
}
How can I select specific indexpath of a UICollectionView by default ?
If you need any other information to solve it, let me know.
Thank you.

This only selects the first cell
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
//add here
let selectedIndexPath = IndexPath(item: 0, section: 0)
collectionView.selectItem(at: selectedIndexPath, animated: false, scrollPosition: [])
}

You should override this method
override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
//you should set needed cells as selected here, or set it to deselected, because it is your responsibility now
var shouldSelect = false //or true
cell.isSelected = shouldSelect
}
But in this case you should keep track of collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) method and reload collection while selection.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.reloadData()
//or reload sections
}

Related

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()
}
}

How do I return back to the first collectionview cell item?

I'm working in UICollectionViewCell for my mobile app. When you tap the crossBtn it should go to the first UICollectionViewCell with reloading the data but collectionView?.reloadData() makes the colours messed up and it doesn't stay to one colour at a time per cell. Instead it keeps showing multiple colours on the cells even though I've put the colour as UIColor.clear in didDeselectItemAt method.
How do I return back to the first UICollectionViewCell item when the user taps on crossBtn without reloading the whole UICollectionView?
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return filters.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
cell.hamburgerLabel.text = filters[indexPath.item]
// cell.hamburgerImageView.image = filterImages[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.layer.backgroundColor = UIColor.gray.cgColor
setFilter(title: filterNames[indexPath.row])
toolBar.isHidden = true
yesAndNoToolBar.isHidden = false
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.layer.backgroundColor = UIColor.clear.cgColor
collectionView.deselectItem(at: indexPath, animated: true)
}
#IBAction func crossBtn(_ sender: UIBarButtonItem) {
toolBar.isHidden = false
yesAndNoToolBar.isHidden = true
collectionView?.reloadData()
let indexPath = self.collectionView.indexPathsForSelectedItems?.last ?? IndexPath(item: 0, section: 0)
self.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: UICollectionViewScrollPosition.centeredVertically)
}
You may not have to reload the data. You can use the following method to scroll to a given index path.
let indexPath = self.collectionView.indexPathsForSelectedItems?.last ?? IndexPath(item: 0, section: 0)
func scrollToItem(at indexPath: indexPath,
at scrollPosition: .centeredVertically,
animated: true)
Note that this is different from using selectItem(at:animated:scrollPosition:).
Also, you should set the colours etc that you are having trouble with in the cellForItem method, not in the selection and deselection delegate methods. This is because cells are reused, so the cell you are styling will be used in a different place when you scroll. Instead, use the selection and deselection methods to store the selected index path(s) in a variable (or array of multiple), then check this array in cellForItem to determine whether to apply selected styling to that cell. This is why collectionView.reloadData() messes things up for you!

Swift 4 - deselect collection view cell if already selected

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

UICollectionview - prefetchItemsAt indexPaths - not being called

I'm trying to use prefetching which was introduced in iOS 10. But unable to achieve it.
I do set the delegate and enable the prefetching. But when i set a breakpoint or console log at prefetch, it does not pass through the function ever.
I know on fast scroll, the prefetching is skipped. but even on slowest scroll, its never been called
var dataSource = [IndexPath: Any]()
override func viewDidLoad() {
if #available(iOS 10.0, *) {
mainCollectionView.isPrefetchingEnabled = true
mainCollectionView.prefetchDataSource = self
}
//Other stuffs
}
//This is not being called
func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
print("Prefectch")
for indexPath in indexPaths {
dataSource[indexPath] = fetchDataSource(indexPath)
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath) as! MyCell
cell.dataSource = dataSource[indexPath] ?? fetchDataSource(indexPath)
return cell
}
Call
override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
self.collectionView(collectionView, prefetchItemsAt: [indexPath])
}
To trigger the prefetch, and remove:
collectionViewLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
If you have it.
This was by me the case.

Stop a collection view deselecting an item

I have a UICollectionView that is a calendar. It's a single scrollable row. Some of the dates are unselectable as there is no data for that date. Others ARE selectable as there IS data for that date.
When a date is selected the calendar underlines the selected date and scrolls it to the center of the UICollectionView.
When a date is tapped that doesn't have a date I have this function...
override func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
return //is there data for the date at this indexPath?
}
This stops those cells being selected... however, it still deselects the previously selected cell.
There is a shouldDeselect function but I can't use this as I don't know the index of the tapped cell. So I can't determine whether the item should be deselected.
Is there a better way of doing this?
It simple works for me when I reload the collectionview:
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
// return true whithout reloadData when necessarily
collectionView.reloadData()
return false
}
I recently had the same issue and I ended up with the following approach:
Whenever user selects a cell, remember the position in a member variable:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
myMemberVariable = indexPath.row
}
After that call myCollectionView.reloadData()
In the cellForRowAtIndexPath: apply appropriate configuration for the cell:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCell.identifier, for: indexPath) as! MyCell
if indexPath.row == myMemberVariable && canSelectCellAt(indexPath: indexPath) {
cell.backgroundColor = .green
} else {
cell.backgroundColor = .gray
}
return cell
}
Where canSelectCellAt(indexPath: indexPath) is a function, which returns true for selectable cells and false for non-selectable cells,
I just came across this question (kind of late?) and to solve this you just need to use
func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
Instead of shouldSelect
My solution:
collectionView.allowsMultipleSelection = true
and in didSelect, deselect all previously selected index paths:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.indexPathsForSelectedItems?.filter({ $0 != indexPath }).forEach { indexPath in
collectionView.deselectItem(at: indexPath, animated: false)
}
}
My solution is to enable multiple selection
collectionView.allowsMultipleSelection = true
and
func collectionView(_ collectionView: UICollectionView, shouldDeselectItemAt indexPath: IndexPath) -> Bool {
return false
}

Resources