I am working on a grid based game (like minesweeper). It is a 2d grid that I am displaying using a UICollectionView that contains a set of custom UICollectionViewCell cells.
I want to be able to listen to touch events on individual cells. Is it fine to add an UITapGestureRecognizer() to each of the cells (on a 20x20 board, for example). Or is there a better way?
I understand from Ahmad's reply that using this works for single taps:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(indexPath.row)
}
However, I want different event handlers for single vs double taps on the cells.
If you want to let the base container as a UICollectionView (which I find it a good idea), I doubt that you need to add UITapGestureRecognizer to it, all you have to do is to let your class (Controller) to conforms to UICollectionViewDelegate its Delegate and implement collectionView:didSelectItemAtIndexPath:.
Also, the benefit that you'll get when implementing this method is you can easily determine which cell has been selected, by checking what is the indexPath.row of the selected cell, as follows:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(indexPath.row)
}
Don't forget to:
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource { //...
EDIT:
In case of you want to add a double tap functionality to your collection view, I suggest to check this answer to achieve it.
Hope this helped.
Related
You can show my design in the image(storyboard). I have a Table View Controller which has a collection cell inside the table view cell.
To make a horizontal slider, I designed program like this. However the problem is I do not know how can I send my data(which is in the collection cell) to the Master.
class TableViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
I think I need to make sth. in here, but I cannot create a segue and also cannot reach SplitViewController. Maybe I'm in the wrong place. Is there any hint or new story about this program?
Please note that I use alamofire to get data, and also you can look it second pic to what I'm trying to do.
If I am using UICollectionViewController with diffableDataSource and tapping on cell - it highlights, but then gets unhighlighted while I'm still holding finger on it.
If to use UIViewController + collectionView with diffableDataSource - then everything working as intended, and cell keeps highlighted until I release finger from it. The same if to use UICollectionViewController with standard dataSource - everything working good.
Has anyone noticed this problem as well? Any advice or thoughts would be appreciated, maybe I'm just missing something, but for now it feels more like a bug from Apple side
You can see the example here:
https://github.com/ashishbl86/MockAppStore/blob/0ea8e74a4823c8c80bd7e8d5c6a9514958fbe459/MockAppStore/CollectionViewController.swift
Just add to CollectionViewController.swift file these methods:
override func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
print("cell highlighted")
}
override func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath) {
print("cell unhighlighted")
}
and you'll see that "cell unhighlighted" is printed while your finger is still on a cell
okay as it turned out we need to set
installsStandardGestureForInteractiveMovement = false
this variable installs a standard gesture recogniser for UICollectionViewController to drive the reordering process.
It is set to true by default, but in order to make things work the collectionView dataSource must declare its support for reordering items by implementing the appropriate methods.
In old good dataSource methods moveItemAt are marked as optional, while in diffableDataSource it is not declared as optional any more, which is the reason for this variable to cause the behaviour described in the issue above.
More info about this variable available in docs:
https://developer.apple.com/documentation/uikit/uicollectionviewcontroller/1623979-installsstandardgestureforintera
I have come across a very disturbing issue in which I am not able to dequeue only a single cell in collectionView. When there are more than one item in collectionView, it works fine but soon as I have an array with a single item, it doesn't show any cell. I am not able to figure it out what’s wrong with my code.
extension collectionview: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! UICollectionViewCell
return cell
}
}
The number returned for numberOfItemsInSection should be the total number of objects you want to show, so if you have an array of objects for example, you would want to return the count of that array. Since it's currently set to one, only one item will be shown. If you change that to match the number of items you're displaying, then the collectionView should cooperate.
First:
the returned of numberOfItemsInSectionwill be array.count for Example
var array = [model]()
Then in cellForItemAt you should configure your cell with data so
Add this block in your cell class
func configureCell(_ data: model) {
// code for cell view
}
Finally cellForItemAt will be
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! UICollectionViewCell
cell.configureCell(array[indexPath.item])
return cell
}
I hope this be helpful for you.
Needless to say, collection views work fine with only one cell. Your code is insufficient to manifest the problem you describe.
But here are a few observations/suggestions:
Add breakpoint or debugging log statement in numberOfItemsInSection and make sure it’s getting called. If not, then you likely have one of a variety of different possible problems:
Make sure you set the “data source” for the collection view (either in IB or programmatically). Merely conforming to UICollectionViewDataSource is not sufficient: You actually have to set the data source of the collection view.
Make sure you set the base class for your storyboard scene to be the view controller in question.
Make sure you put these UICollectionViewDataSource methods in the right object.
I notice that you've put these methods as an extension to collectionview.
Class names should, as a matter of convention, always start with upper case letter.
Usually the UICollectionViewDataSource methods are put in the view controller (or, less common, a dedicated object that you'll vend and to which keep your own strong reference). I'm not sure what to make of your class named collectionview.
But, in short, there is no problem having collection views with only one cell. Your problem rests elsewhere.
I want to disable delete and move the 1st cell in UICollectionView. And also other cells in UICollectionView cannot move to the 1st one. How to implement it?
To disable movement you need to implement UICollectionViewDataSource protocol and use this method:
func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {
return indexPath.item != 0
}
Disabling deletion depends totally on your deletion implementation.
I have UICollectionView and the selected cell should like the yellow one in pic. So how to have a separate design for the selected cell and how to draw that curve above it ?
Shall I use 2 separate UICollectionViewCell for this ? Or there is any alternate way to reuse the same cell on selection.
Shall I use 2 separate UICollectionViewCell for this ?
That's one way to go. Do this if there are more differences than just the one you described.
Or there is any alternate way to reuse the same cell on selection.
Sure, you can do that. Look at the two cells in your illustration, but consider that the grey part above each one as part of the cell. The black rectangle and yellow bulging rectangle are simply two different images that you draw in the background of the cell, and you can configure the same type of cell either way simply by changing that image. This is a good approach if other aspects of the cell, like positions of labels and such, are the same between both cells.
If this is the only different between that you want to make after selection, I think that there is no need to create two different UICollectionViewCells, instead, you need to keep a reference on indexpath.row(s) of selected cell(s) and check if this is the selected row, change/add a new background image.
For example:
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
// here is the variable that should save the current selected row...
private var selectedRow: Int?
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// let's consider that you have a custom cell called "MyCustomCell"
// which has "backgroundImage" property...
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myCellID", for: indexPath) as! MyCustomCell
// checking based on the selected row
cell.backgroundImage = indexPath.row == selectedRow ? UIImage("yellow") : UIImage("default")
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedRow = indexPath.row
collectionView.reloadData()
}
}
Note that if you want to check on more than one row, you should declare a selectedRows as an array (or maybe as a set) of Ints.
Hope it helps.