Tap on cell and get index - swift

When i tap on a cell i want to receive the index or other identifier specific to that cell. The code works and goes in the tapped function. But how can i receive a index or something like that?
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("ShowCell", forIndexPath: indexPath) as UICollectionViewCell
if cell.gestureRecognizers?.count == nil {
let tap = UITapGestureRecognizer(target: self, action: "tapped:")
tap.allowedPressTypes = [NSNumber(integer: UIPressType.Select.rawValue)]
cell.addGestureRecognizer(tap)
}
return cell
}
func tapped(sender: UITapGestureRecognizer) {
print("tap")
}

Swift 4 - 4.2 - Returns index of tapped cell.
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cellIndex = indexPath[1] // try without [1] returns a list.
print(indexPath[1])
chapterIndexDelegate.didTapChapterSelection(chapterIndex: test)
}

Build on Matts answer
first off we add the tap recognizer
let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapped(tapGestureRecognizer:)))
cell.textView.addGestureRecognizer(tap)
Then we use the following function to get the indexPath
#objc func tapped(tapGestureRecognizer: UITapGestureRecognizer){
//textField or what ever view you decide to have the tap recogniser on
if let textField = tapGestureRecognizer.view as? UITextField {
// get the cell from the textfields superview.superview
// textField.superView would return the content view within the cell
if let cell = textField.superview?.superview as? UITableViewCell{
// tableview we defined either in storyboard or globally at top of the class
guard let indexPath = self.tableView.indexPath(for: cell) else {return}
print("index path =\(indexPath)")
// then finally if you wanted to pass the indexPath to the main tableView Delegate
self.tableView(self.tableView, didSelectRowAt: indexPath)
}
}
}

Think about it. The sender is the tap gesture recognizer. The g.r.'s view is the cell. Now you can ask the collection view what the index path of this cell is (indexPathForCell:).

Related

How to give xib cell button action in HomeVC in swift

I have created xib collectionview cell.. and i am able to use all its values in HomeVC like below
class HomeVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource,UICollectionViewDelegateFlowLayout{
#IBOutlet var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib(nibName: "MainCollectionViewCell", bundle: nil)
collectionView.registerNib(nib, forCellWithReuseIdentifier: "MainCollectionViewCell")
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("MainCollectionViewCell", forIndexPath: indexPath) as! MainCollectionViewCell
cell.arrivingLabel.text = indexData.arriv
cell.lblDiscountedPrice.text = indexData.discPrice
return cell
}
like below i can give action to xib cell button, but i want xib cell button action in HomeVC class how, please guide me here
cell.btnDetails.addTarget(self, action: #selector(connected(sender:)), for: .touchUpInside)
#objc func connected(sender: UIButton){
}
i want like this in HomeVC
#IBAction func productDetailsMain(_ sender: UIButton){
}
note: if i use same collectionview cell then if i drag from HomeVC button action outlet to collectionview cell button then its adding.. but if i use xib cell in collectionview then this process is not working.. how to give xib cell button action in HomeVC class
You have to use closure.
cell class add this property
var connectionButtonAction: (() -> Void)?
and on the button action make this
#objc func connected(sender: UIButton){
connectionButtonAction?()
}
so finally for on the cell creation you have add this:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("MainCollectionViewCell", forIndexPath: indexPath) as! MainCollectionViewCell
cell.arrivingLabel.text = indexData.arriv
cell.lblDiscountedPrice.text = indexData.discPrice
cell.connectionButtonAction = { [weak self] in
print("cell button pressed")
}
return cell
}
I think this is the simple way, but also you get the same approach using delegates.
If you want to have the collection view cell's button trigger an action in the view controller that own's the collection view, you need to add the view controller as the target. You can do that in your cellForItemAtIndexPath, but you will need to remove the previous target/action so you don't keep adding a new target/action each time you reuse a cell:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("MainCollectionViewCell", forIndexPath: indexPath) as! MainCollectionViewCell
cell.arrivingLabel.text = indexData.arriv
cell.lblDiscountedPrice.text = indexData.discPrice
// Remove the previous target/action, if any
cell.btnDetails.removeTarget(nil, action: nil,for: .allEvents)
// Add a new target/action pointing to the method `productDetailsMain(_:)`
cell.btnDetails.addTarget(self, action: #selector(productDetailsMain(_:)), for: .touchUpInside)
return cell
}
You could also set up your cells to hold a closure as in luffy_064's answer.
Note that if you are running on iOS 14 or later, you can use the new addAction(_:for:) method of UIControl to add a UIAction to your button. (UIActions include a closure.)
That might look like this:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("MainCollectionViewCell", forIndexPath: indexPath) as! MainCollectionViewCell
cell.arrivingLabel.text = indexData.arriv
cell.lblDiscountedPrice.text = indexData.discPrice
// Remove the previous target/action, if any
let identifier = UIAction.Identifier("button")
removeAction(identifiedBy: identifier, for: .allEvents)
// Add a new UIAction to the button for this cell
let action = UIAction(title: "", image: nil, identifier: identifier, discoverabilityTitle: nil, attributes: [], state: .on) { (action) in
print("You tapped button \(indexPath.row)")
// Your action code here
}
cell(action, for: .touchUpInside)
return cell
}

Field an UITextField when button tapped in a UICollectionView

I gonna need your help to understand how to interact with an UITextField when button tapped.
This is my code in my ViewController:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCollectionViewCell
cell.addBookTapped.addTarget(self,action: #selector(buttonTapped(_:)),for: .touchUpInside)
cell.configureCell(with: books[indexPath.row], indexCell: indexPath.row )
return cell
}
func buttonTapped(_ sender: UIButton)
{
let tag = sender.tag
//Do Stuff
}
And in my MyCollectionViewCell, I configure all the button and textView and add tag in my button:
#IBOutlet weak var myTextFielThatIWantToFieldWhenButtonTapped: UITextField!
In my ViewController inside my func buttonTapped I can't reach myTextFielThatIWantToFieldWhenButtonTapped.
How can write something in it when the button is tapped and be visible directly on the view?
you have to get your intended cell by calling cellForItem at an indexPath of your specific cell like below: (for example your cell is in section 0 item 0 )
let indexPath = IndexPath(item: 0, section: 0)
let cell = collectionView.cellForItem(at: indexPath) as? YourCustomCollectionViewCellClass
then you can access your variables inside your cell class :
cell.myTextFielThatIWantToFieldWhenButtonTapped

UITapGestureRecognizer not changing contents of CollectionViewCell . Swift

I have a collection view with lots of different pictures, and I want that when a picture is tapped, a border comes around it.
I made a custom cell, and a UICollectionViewCell with the imageView embedding in another view (which is the outline) for the pic. So I set up a UITapGestureRecognizer which works to get the index, but when I set the outer view to have a boder it doesn't work.
Here is my Cell:
import UIKit
class PicViewCell: UICollectionViewCell {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var imageOutline: UIView!
}
This is in my UICollectionViewController:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! PicViewCell
// Configure the cell
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(sender:)))
cell.imageOutline.addGestureRecognizer(tapGesture)
return cell
Here is the method for the tapGesture
func handleTap(sender: UITapGestureRecognizer) {
print("tap")
if let indexPath = self.collectionView?.indexPathForItem(at: sender.location(in: self.collectionView)) {
let cell = collectionView?.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! PicViewCell
cell.imageOutline.layer.borderWidth = 5
} else {
print("")
}
}
In your handleTap routine, you're calling the wrong method to retrieve the cell. You want cellForItem(at:) which will retrieve the existing cell or return nil if it isn't onscreen:
let cell = collectionView?.cellForItem(at: indexPath) as! PicViewCell
Alternate solution
If you are modifying the view you are tapping, you can access it as the view of the sender:
func handleTap(sender: UITapGestureRecognizer) {
if let imageOutline = sender.view as? UIImageView {
imageOutline.layer.borderWidth = 5
}
}
Note: In both solutions, you should update your model when a cell is outlined so that if that cell scrolls off the screen and then back onto the screen, you can use that model data to set the outline for the cell correctly in your override of collectionView(_:cellForItemAt:).

button inside cellView I can't know which cell user pressed

I have custom cell in tableView
inside it I have a button , I want when user click on the button push viewController, how I can do that and how I can know which cell user use its button, because here not didSelectRowAtIndexPath
Create a custom button class
class CustomButton: UIButton {
var indexPath: NSIndexPath!
}
In your custom cell create the button of the CustomButton type add the following lines in cellForRowAtIndexPath
cell.yourCustomButton.indexPath = indexPath
Define IBAction like this
#IBAction func customButtonClicked(sender: CustomButton) {
let indexPath: NSIndexPath = sender.indexPath
// Do whatever you want with the indexPath
}
Add Tag of button in Cell and add Target for your transition Function
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier) as! CustomCell
cell.button.tag = indexPath.row
cell.button.addTarget(self, action: #selector( self.transitonMethod ), forControlEvents: UIControlEvents.TouchUpInside)
return cell
}
Fetch indexPath from tag of sender Button and Fetch Cell for this indexPath.Push your controller on your navigation controller
func transitonMethod(sender: AnyObject){
let indexPath = NSIndexPath.init(index: sender.tag)
let cell = tableView.cellForRowAtIndexPath(indexPath)
self.navigationController?.pushViewController(yourController, animated: true)
}
In cellforRow method of tableview:
1) set tag of your button e.g. yourButton.tag = indexpath.row
2) set target method of yourbutton e.g.buttonPressed(sender:UIButton)
3) Now in that target method you will get indexpath.row by sender.tag
Declare an IBAction as follow as suggested in IOS 7 - How to get the indexPath from button placed in UITableViewCell :
#IBAction func didTapOnButton(sender: UIButton) {
let cell: UITableViewCell = sender.superview as! UITableViewCell
let indexPath: NSIndexPath = self.tableView.indexPathForCell(cell)
// Do anything with the indexPath
}
Or the other method :
#IBAction func didTapOnButton(sender: UIButton) {
let center: CGPoint = sender.center
let rootViewPoint: CGPoint = sender.superview?.convertPoint(center, toView: tableView)
let indexPath: NSIndexPath = tableView.indexPathForRowAtPoint(rootViewPoint!)
print(indexPath)
}
Then work with that indexPath to do whatever you need.

How to pass indexPath.row through the tag property with UILongPressGestureRecognizer in Swift?

This is my code:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! imageUserCell
let coolGesture = UILongPressGestureRecognizer(target: self, action: #selector(detailedPerson.makeItCoolAction(_:)))
coolGesture.minimumPressDuration = 0.5
coolGesture.view?.tag = 4
cell.addGestureRecognizer(coolGesture)
And this is the function code:
func makeItCoolAction(sender: UIGestureRecognizer){
print(sender.view?.tag)
print("Photo cooled")
}
The console prints the same value: 0, which is the default one. I strongly need to pass the indexPath.row value to the makeItCoolAction but I saw UIGestureRecognizer doesn't have tag property but .view property has it as you may see in the code.
I've changed the makeItCoolAction sender from UILongPressGestureRecognizer to UIGestureRecogizer and it still keep printing the 0 value.
view is nil at this point:
coolGesture.view?.tag = 4
try setting the tap after you've added the gesture and it has a view like this:
cell.addGestureRecognizer(coolGesture)
coolGesture.view?.tag = 4