Field an UITextField when button tapped in a UICollectionView - swift

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

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
}

ReloadData is not working for a UICollectionView

I would like to change my entier array used to created all my cells with a button and then reload my collection view, but it is not working. I've tried to reload the collection view into the main thread but it also not working
I've tried to reload the collection view into the main thread, or just using collectionView.reloadData() but it also not working
#IBAction func BicepsBtn(_ sender: Any) {
self.current = "Biceps"
Data = user.getMuscleDataChart(typeMuscle: current)
listMuscle = user.getListMuscle(typeMuscle: current)
collectionView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return listMuscle.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let switchView = cell.viewWithTag(1000) as? UISwitch
let titleView = cell.viewWithTag(1001) as? UILabel
if let titleView = titleView, let switchView = switchView {
titleView.text = self.listMuscle[indexPath.row].label
titleView.textColor = self.listMuscle[indexPath.row].color
switchView.tag = indexPath.row
switchView.addTarget(self, action: #selector(yourFunc),
for: UIControl.Event.valueChanged)
}
return cell
}
Here is my current code, when the user is going to click on the Biceps button, the function will fetch all data about this muscle and then trying to reload.
Any idea ?
Thanks in advance

Using variable from one class in another class

I have two classes: CollectionCell and ViewController.
The first class is used for Collection View Cell (in which I have placed a button). In CollectionCell class I need to connect outlet for this button: #IBOutlet weak var firstPercent: UIButton!.
In ViewController class I need to use var of this button: firstPercent.
How can I do that? Thanks for any help.
In collectionViewDelegate
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
Now declare your Custom Cell instance and then
you can use CollectionCell.firstPercent addTarget in ViewController and set this or
do whatever you want.
You can get it something like below.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ZonesCell", for: indexPath as IndexPath) as! ZonesCell
let keyValue = self.dataList[indexPath.row] as! [String:Any]
cell.lblZoneName.text = keyValue[Keys.name] as? String
if(keyValue[Keys.status] as? Bool)! {
cell.lblZoneName.textColor = UIColor.white
} else {
cell.lblZoneName.textColor = UIColor.red
}
return cell
}
There lets suppose ZonesCell is the collectionCell contains lblZoneName as label.
You can use button same as label.
You can use delegate for this.
Add a protocol named “CollectionCellDelegate“ for your cell and add a delegate method for your button action. Pass cell itself as a parameter to that method. Conform to “CollectionCellDelegate“ from your ViewController and implement delegate method inside ViewController. Finally but most important step, assign your view controller as CollectionCellDelegate from cellForItemAt method.
protocol CollectionCellDelegate : class {
func didPressButtonFirstPercent(_ cell: CollectionCell)
}
//Inside your CollectionCell class declare a variable to hold your cell delegate
class CollectionCell: UICollectionViewCell {
//Add a variable to hold your delegate
weak var delegate: CollectionCellDelegate?
//Outlet for your button
#IBOutlet weak var firstPercent: UIButton!
//Action for your button
#IBAction func didPressButtonFirstPercent(_ sender: Any) {
delegate?.didPressButtonFirstPercent(self)
}
}
//ViewController Conform to CollectionCellDelegate
class ViewController: UIViewController, CollectionCellDelegate{
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: “Cell_Identifier”, for: indexPath) as! CollectionCell
//Set ViewController as your CollectionCell delegate
cell.delegate = self
return cell
}
//Implement delegate method here
func didPressButtonFirstPercent(_ cell: CollectionCell) {
//You can access your button here like below
Let firstPercent = cell.firstPercent
}
}
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate{
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! CollectionCell
// Use the outlet in our custom class to get a reference to the UILabel in the cell
cell. firstPercent.tag = indexPath.row
example project
return cell
}
}

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

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