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()
}
Related
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()
I have a collectionView used for scrolling between pages, inside of one of these full page cells I have another collectionView with cells. How do I perform a segue when one of the cells inside of the inner most collectionView is tapped.
You will need a delegate on the cells with collection view, that will need to be notified when a particular cell is selected:
protocol CollectionCellDelegate: class {
func selectedItem()
}
class CollectionCell: UITableViewCell {
weak var delegate: CollectionCellDelegate?
// ...
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
self.delegate?.selectedItem()
}
}
And in the TableViewController you will have to implement that delegate to perform segue from it (you have to perform segue from UIViewController subclass, but the UITableViewCell is not subclassing it, that's why you need the delegate pattern).
class TableViewController: UITableViewController, CollectionCellDelegate {
// ...
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CollectionCell", for: indexPath) as! CollectionCell
// set its delegate to self
cell.delegate = self
return cell
}
func selectedItem() {
// here you can perform segue
performSegue(withIdentifier: "mySegue", sender: self)
}
}
I haven't passed any argument to the delegate, but you can of course use arguments to pass any information that you need for the segue (e.g., the id of the collection cell that was selected, etc.).
When you tap on an item in collectionView, the following delegate method will be called (if you wired up everything properly) func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)... notice that the first param is collectionView itself.
Depending on how you set it up...
if you have two collectionViews within one UIViewController then you can do..
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
if collectionView == self.innerCollectionView {
// performSegue...
}
}
if you have two view controllers, one for outer and another for inner.. then you can create use delegate pattern to let the outer know which item got selected, and segue using that info.
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
I have 2 swift files the one is my HomeViewController and the second is my EventCollectionViewCell. In the second one I have an IBOutlet = informationView :UIView and I want to access this from the HomeViewController.
Is there any way ?
Thank you in advance,
KS
Well I suppose that in HomeViewController you have a UICollectionView and that acts as a datasource for it, then in your collectionView dataSource you can make:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cellIdentifier", forIndexPath: indexPath) as! EventCollectionViewCell
//Here you can access the IBOulets define in your cell
cell.informationView.backgroundColor = UIColor.greenColor()
return cell
}
Edit:
Problem: When you tap on a cell you want to show an Overlay inside the cell.
Solution:
You need you data model to maintain the state, if it's active or not (the informationView), suppose that ItemCell is my model (in your case Event can be):
class ItemCell{
var active:Bool = false
}
In the collectionView:cellForRowAtIndexPath:, you're going to check the current status of your model, and base on that, show or hide that overlay:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cellIdentifier", forIndexPath: indexPath) as! EventCollectionViewCell
let event = self.data[indexPath.row]
//Here you can access the IBOulets define in your cell
cell.informationView.hidden = !event.active
return cell
}
Then as a final step, every time you select a cell (func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) method), you're going to update the status of your model:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath){
let event = self.data[indexPath.row]
event.active = !event.active
collectionView.reloadData()
}
Sample Project:
https://github.com/Abreu0101/SampleTap
Follow what Jose has answered and if you are creating the cell in the interface builder. Set the class of the cell in Identity Inspector to EventCollectionViewCell and set the cell identifier to "cellIdentifier" that Jose has specified.
Declare a property for that cell and access IbOutlets through that object.
#property (nonatomic, strong) MSSearchHeaderView *searchHeaderView;
if (self.searchHeaderView.searchTextField.text.length <= 0) {
self.searchHeaderView.clearButton.hidden = YES;
}else{
self.searchHeaderView.clearButton.hidden = NO;
}
hello I have implemented a UICollectionView in my controller. There are lots of info I am displaying on each cell. When user clicks the particular cell I want to pass all the info that was in the cell to another controller.
As I am displaying all the data from db in the cells and cells are generating dynamically so I think one way could be to pass just a record id from one cell to this function didSelectItemAtIndexPath and then in the second controller I query again from the database. But I think that would not be the good idea. and also I don't know how can I pass the id or any info here in this function didSelectItemAtIndexPath.
So in short I want to display all the info in another controller that is being displayed in that cell which is being clicked
here is my code
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath
indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell",
forIndexPath: indexPath) as! CardsCollectionCell
cell.usernameLabel.text = (((dict["\(indexPath.item)"] as?NSDictionary)!["Trip"] as?NSDictionary)!["userName"] as?NSString)! as String
cell.feeLabel.text = (((dict["\(indexPath.item)"] as?NSDictionary)!["Trip"] as?NSDictionary)!["fee"] as?NSString)! as String
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
}
So let's going to explain by parts, the didSelectItemAtIndexPath delegate method is used to know what UICollectionViewCell was tapped in the UICollectionViewController, you can get the UICollectionViewCell very easily using the method cellForItemAtIndexPath(it's not the same as collectionView(collectionView: UICollectionView, cellForItemAtIndexPath) like in the following code:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell = self.collectionView?.cellForItemAtIndexPath(indexPath) as! CardsCollectionCell
// access to any property inside the cell you have.
// cell.usernameLabel.text
}
So regarding in short I want to display all the info in another controller that is being displayed in that cell which is being clicked
You can use the didSelectItemAtIndexPath method without any problem to launch a manual segue using the method performSegueWithIdentifier to another UIViewController and just in the prepareForSegue pass any data you need to pass to the another UIViewController. See the following code:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// the identifier of the segue is the same you set in the Attributes Inspector
self.performSegueWithIdentifier("segueName", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "segueName") {
// this is the way of get the indexPath for the selected cell
let indexPath = self.collectionView.indexPathForSelectedRow()
let row = indexPath.row
// get a reference to the next UIViewController
let nextVC = segue.destinationViewController as! NameOfYourViewController
}
}
I hope this help you.