UICollectionView viewForSupplementaryElementOfKind didSelect - swift

How is a selection within a viewForSupplementaryElementOfKind recognized? I've tried adding a target to the button within the header but that doesnt work. Is the selection action sent to didSelectItemAtIndexPath or is there something else? All elements which header are within a UIStackView
override func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
if kind == UICollectionElementKindSectionHeader {
let cell = collectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "header", forIndexPath: indexPath) as! mainCell
let index = indexPath.section
cell.btnImg.af_setBackgroundImageForState(.Normal, URL: NSURL(string: "urrl")!)
cell.userImg.addTarget(self, action: #selector(MainController.showNextView(_:)), forControlEvents: .TouchUpInside)
let tap = UIGestureRecognizer(target: self, action: #selector(self.test))
cell.addGestureRecognizer(tap)
return cell
} else {
return UICollectionReusableView()
}
}

You are correct that you need to use a UIControl or UIGestureRecognizer. It looks like you are adding both in your example code and the UIGestureRecognizer is consuming the touches instead of your button.

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

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

Tap on cell and get index

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:).

Error casting value of type 'UICollectionReusableView' to 'MyApp.CSSectionHeader'

I have the following class:
class CSSectionHeader: UICollectionReusableView {
#IBOutlet var textLabel: UILabel!
}
I'm then trying to cast it like the following:
override func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
let cell1 = UICollectionReusableView()
if (kind == UICollectionElementKindSectionHeader) {
// Throws the cast error here:
let cell: CSSectionHeader = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "sectionHeader", forIndexPath: indexPath) as! CSSectionHeader
return cell
} else if (kind == CSStickyHeaderParallaxHeader) {
let cell: UICollectionReusableView = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "header", forIndexPath: indexPath) as! UICollectionReusableView
return cell
}
return cell1
}
The problem is I'm getting the following error:
Could not cast value of type 'UICollectionReusableView' (0x10ff4d140) to 'MyApp.CSSectionHeader' (0x10d775a70).
In order to be able to deque header of your custom class you have to register this class in collectionview before calling dequeueReusableSupplementaryViewOfKind.
This should look like:
self.collectionView.registerClass(CSSectionHeader.self,
forSupplementaryViewOfKind: UICollectionElementKindSectionHeader,
withReuseIdentifier: "sectionHeader")
You can place it inside viewDidLoad()
You can also set the custom class for the header in the storyboard.
This comes in handy if you are adding the Flow Layout delegate via an extension.
Swift 3
in viewDidLoad()
self.collectionView.registerClass(CSSectionHeader.self,
forSupplementaryViewOfKind: UICollectionElementKindSectionHeader,
withReuseIdentifier: "sectionHeader")
Also add the following
func collectionView(_ collectionView: UICollectionView,
viewForSupplementaryElementOfKind kind: String,
at indexPath: IndexPath) -> UICollectionReusableView{
let headerView: CSSectionHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier:"sectionHeader", for: indexPath) as! CSSectionHeader
return headerView }