Editable text filed in UICollectionViewCell blocks didSelectItemAt from being called - ios

My UICollectionViewCell has a text field, when I click the cell it lets me edit the text field but the didSelectItemAt function of the UICollectionViewDelegate is not being called. How can I overcome this?
class LetterCell: UICollectionViewCell {
#IBOutlet weak var singleLetterTextField: UITextField!
#IBAction func textDidChange(_ sender: Any) {
if ((singleLetterTextField.text?.count)! > 1) {
singleLetterTextField.text = String((singleLetterTextField.text?.last)!)
}
}
}
This is the collectionView function
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LetterCell", for: indexPath) as! LetterCell
cell.singleLetterTextField.text = data[row][column]
increaseRowColumn()
return cell
}
And I already set the delegate and the data source to the controller.

Considering you need your text field to be editable.
didSelect will work if cell is touched outside of textfield.
It is not unlikely so if you want to recognize didSelect along with editing, you will need to do the calculation in textField didBeginEditing. A basic hack will be to set index path's values as tag or other property of your textfield, in cellForItemAt (check eg.). You can create a custom text field as well.
Here is update to your cellForItemAt:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LetterCell", for: indexPath) as! LetterCell
cell.singleLetterTextField.text = data[row][column]
cell.singleLetterTextField.tag = indexPath.row//then you can use this tag to form indexPath and with that you can retrieve cell (if it's still visible)
increaseRowColumn()
return cell
}

First
singleLetterTextField.isUserInteractionEnabled = false
Then in didSelectItemAt
cell.singleLetterTextField.becomeFirstResponder()

Related

how to interact with custom UICollectionViewCell cell

I have a UICollectionViewController, which scrolls vertically(like a tableview). I created a custom UICollectionViewCell. Inside of a custom cell, there are checkmarks. I need some kind of event to click when the user clicks on a checkmark.
What I tried, was overriding:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("clicked")
}
but that only executes when the use clicks on a cell. But, asI stated above, the cell contains checkmarks...I need to find out when each individual check mark is clicked.
Is this possible?
You can add an IBAction to the UICollectionViewCell class and handle the tap from that IBAction, if you need it to change something on the Parent view controller you have a couple of options, you can use a delegate or pass the controller to the cell.
On the parent view controller :
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! CustomCollectionViewCell
cell.controller = self
return cell
}
On the UICollectionViewCell class:
var controller: ParentViewController?
#IBAction func checkmarkPressed(sender: UIButton) {
print("checkmarkPressed")
controller.someFunction()
}

Swift UICollectionViewCell didSelectItemAt not printing label name on cell

I have looked through google and stack overflow and I was not able to find an answer. I have a UICollectionView and would like to take the user to another view upon the cell being clicked. But before doing so I would like to click the cell on the simulator and have the label's name printed in the console so I can from there figure out how to write the performSegue method. I am having issues with the didSelectItemAt function.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let clothesCell = collectionView.dequeueReusableCell(withReuseIdentifier: "clothesCell", for: indexPath) as! closetCollectionViewCell
clothesCell.clothingName.text = shirtStyle[indexPath.item]
clothesCell.clothingColor.text = shirtColor[indexPath.item]
clothesCell.clothingSize.text = "\(sizes[indexPath.item])"
return clothesCell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(indexPath.item.clothingName)
}
Use this for print in didselectItem for selected items
print(shirtStyle[indexPath.item])
You need to get the cell that was clicked at indexPath like this:
let cell = collectionView.cellForItem(at:indexPath) as! closetCollectionViewCell
Then just get the values from the cell variables and print.

Get next cell in UIcollectionView in Swift

I need to get the next cell inside cellForItem within a collection view so that I can update a view object. When I try the following below it doesn't work. I've also tried indexPathForVisibleItems passing in indexPath.row + 1 and the produces an index out of range error.
let index = IndexPath(row: indexPath.row + 1, section: indexPath.section)
if let nextCell = collectionView.cellForItem(at: index) as! MKRCell {
nextCell.setupWaitView(time: timeToWait)
nextCell.waitViewHeightConstraint.constant = 80
nextCell.waitView.alpha = 1
nextCell.waitView.isHidden = false
}
Is this possible to achieve or will I need to do this via another way?
Thanks
No, it is not possible to get the cell object before initialization in cellForItemAt but here
you can receive the call before displaying the cell from UICollectionViewDelegate
func collectionView(_ collectionView: UICollectionView,willDisplay cell: UICollectionViewCell,forItemAt indexPath: IndexPath) {
if let myCell = cell as? MKRCell {
}
}
AND
If you want to set up the cell you have to setup view in the UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
}
You should update the cell in:
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
Remember to modify only the cell you'll be returning from this method. Other cells might not have exist at that moment.
Alternatively you can keep a weak reference to the cell and update it when needed.

Change Variable based on Cell Clicked

I want to set a variable to different string when a certain CollectionView cell is tapped. So cell1 is tapped then var cellTapped = "cell1", cell 2 is tapped then var cellTapped = "cell2" and so on. It was suggested that I
"create a custom cell and hold the value as property and read this
value on didSelectCell()"
but I'm not sure how to do this (newbie).
(Swift 3)
You need to set UICollectionViewDelegate to your ViewController and then implement didSelectItemAt IndexPath that gets called when a cell is tapped.
Something like this:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
cellTapped = "cell" + String(indexPath.row)
}
You could also have an array of Strings and index into the array based on the indexPath.row:
let cellStringValues = ["cell1", "cell2", "cell3", ... , "celln"]
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
cellTapped = cellStringValues[indexPath.row]
}
Setup your view controller to be the delegate of the UICollectionView. Your view controller should inherit from UICollectionViewDelegate. Then in the viewDidLoad() for the VC set the delegate variable for the UICollectionView to be the ViewController. To catch selection events override the following UICollectionViewDelegate function.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
cellTapped = "cell\(indexPath.row)"
}
Check out https://www.raywenderlich.com/136159/uicollectionview-tutorial-getting-started for more details on working with collection views

iOS UICollectionView init and update custom cell

I'm trying to implement a custom cell with support for user tapping. Previously the functions related are:
func collectionView(collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueResuableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.setEmpty() // to init the cell
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as ! CustomCell
//implementations
self.collectionView.reloadItem(at: [indexPath])
}
Then I noticed that after the tapping, the second function gets called first, but the first one also gets called afterwards, which means after tapping my cell will still be set to empty, so I changed the first function to this:
let cell = collectionView.dequeueResuableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
if cell.valueInside == nil {
cell.setEmpty() // this will set valueInside to be a non-nil value
}
return cell
But it's still not working properly. I tracked the process: when loading the UI for the first time, init cell first (with the setEmpty() method); then after the tapping, cell is updated, and then the first function is called, but the cell obtained by this
collectionView.dequeueResuableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
shows that the value inside is still nil, so the cell is not really up-to-date. How should I fix this? Or is my implementation logical (should I init the cell somewhere else instead of using this
check if it's nil -> then init
logic)?
func collectionView(collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
is called every time your inquire for a cell like
collectionView.cellForItem(at: indexPath)
or
self.collectionView.reloadItem(at: [indexPath])
The best way to use is to declare a class level variable like an array to hold the backed data, then for cell creating get data from that array and in your didSelectItemAt update that array and then just force the collection view to update that cell only.
// In your class scope
private var arData: [String] = ["One", "Two", "Three"] // This could be populated in viewDidLoad as well
// .... rest of your code
func collectionView(collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueResuableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
//...
if indexPath.row < arData.count {
cell.valueInside = arData[indexPath.row]
} else {
cell.valueInside = "Default Value"
}
return cell
}
// ....
func collectionView(collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//implementations
if indexPath.row < arData.count {
arData[indexPath.row] = "newValue"
self.collectionView.reloadItem(at: [indexPath])
}
}
This is the logic on how to correctly change a cell view content now if you want to change it's appearance again you should set the flag or whatever status distinguishing mechanism you have in the didSelectItemAt and just reload the cell considering cellForItemAt will refer to that status and apply's that appearance change.

Resources