UICollectionViewCell delegate won't fire - swift

I'm obviously doing something wrong but unable as of yet to determine where. I setup the cell as follows:
protocol PropertyPhotoCellDelegate: class {
func deletePropertyPhoto(cell: PropertyPhotoCell)
}
class PropertyPhotoCell: UICollectionViewCell {
weak var propertyPhotoCellDelegate: PropertyPhotoCellDelegate?
let deleteButton: UIButton = {
let button = UIButton()
let image = UIImage(named: "delete.png")
button.setImage(image, for: .normal)
button.showsTouchWhenHighlighted = true
button.isHidden = true
button.addTarget(self, action: #selector(handleDeleteButton), for: .touchUpInside)
return button
}()
var isEditing: Bool = false {
didSet {
deleteButton.isHidden = !isEditing
}
}
I've omitted setting up the cell views. Here is the selector
#objc fileprivate func handleDeleteButton() {
propertyPhotoCellDelegate?.deletePropertyPhoto(cell: self)
}
In the UICollectionViewController, I assign the delegate
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: kCellId, for: indexPath) as! PropertyPhotoCell
cell.photoImageView.image = photos[indexPath.item]
cell.propertyPhotoCellDelegate = self
return cell
}
This hides or shows the delete button on the cell for all the cells in view
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
navigationItem.rightBarButtonItem?.isEnabled = !editing
if let indexPaths = collectionView?.indexPathsForVisibleItems {
for indexPath in indexPaths {
if let cell = collectionView?.cellForItem(at: indexPath) as? PropertyPhotoCell {
cell.deleteButton.isHidden = !isEditing
}
}
}
}
And finally, conforming to the protocol here
extension PropertyPhotosController: PropertyPhotoCellDelegate {
func deletePropertyPhoto(cell: PropertyPhotoCell) {
if let indexPath = collectionView?.indexPath(for: cell) {
photos.remove(at: indexPath.item)
collectionView?.deleteItems(at: [indexPath])
}
}
}
I tap the UICollectionViewController Edit button and all the cells show the delete button as expected. Any of the cell's delete button highlights on tap, but I don't see the delegate getting called.

When the delegate is assigned in the UICollectionViewController, also set the selector for the cell.
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: kCellId, for: indexPath) as! PropertyPhotoCell
cell.photoImageView.image = photos[indexPath.item]
cell.propertyPhotoCellDelegate = self
cell.deleteButton.addTarget(cell, action: #selector(cell.handleDeleteButton), for: .touchUpInside)
cell.deleteButton.isHidden = true
return cell
}

Related

Getting id by clicking on tableView

By clicking on the red area I get a comment id. But I also want to get the id if I click on the blue button. How can I do that?
Right now I use this to detect a tap on a row. But tapping on button should run some other code.
extension FirstTabSecondViewComment: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
FirstTabSecondViewComment.subComment = table[indexPath.row].commentId ?? ""
print(FirstTabSecondViewComment.subComment)
self.performSegue(withIdentifier: "CommentDetail", sender: Any?.self)
}
}
After you have register your custom cell declare it in cellForRowAt and add target in button cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! MyCell // your custom cell
// add target in buoon cell
cell.YourButtonCell.addTarget(self, action: #selector(submit(_:)), for: .touchUpInside)
return cell
}
after that add submit func:
#objc fileprivate func submit(_ sender: UIButton) {
var superview = sender.superview
while let view = superview, !(view is UITableViewCell) {
superview = view.superview
}
guard let cell = superview as? UITableViewCell else {
print("button is not contained in a table view cell")
return
}
guard let indexPath = tableView.indexPath(for: cell) else {
print("failed to get index path for cell containing button")
return
}
// We've got the index path for the cell that contains the button, now do something with it.
print("button is in index \(indexPath.row)")
}
This is a full code example, copy and paste in a new project and run:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let tableView = UITableView()
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
tableView.delegate = self
tableView.dataSource = self
tableView.register(MyCell.self, forCellReuseIdentifier: cellId)
}
#objc fileprivate func submit(_ sender: UIButton) {
var superview = sender.superview
while let view = superview, !(view is UITableViewCell) {
superview = view.superview
}
guard let cell = superview as? UITableViewCell else {
print("button is not contained in a table view cell")
return
}
guard let indexPath = tableView.indexPath(for: cell) else {
print("failed to get index path for cell containing button")
return
}
// We've got the index path for the cell that contains the button, now do something with it.
print("button is in index \(indexPath.row)")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 80
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! MyCell
// add target in buoon cell
cell.cancelButton.addTarget(self, action: #selector(submit(_:)), for: .touchUpInside)
return cell
}
}
class MyCell: UITableViewCell {
let cancelButton: UIButton = {
let b = UIButton(type: .system)
b.backgroundColor = .white
b.setTitle("get Index", for: .normal)
b.layer.cornerRadius = 10
b.clipsToBounds = true
b.titleLabel?.font = .systemFont(ofSize: 16, weight: .semibold)
b.tintColor = .black
b.translatesAutoresizingMaskIntoConstraints = false
return b
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.backgroundColor = .darkGray
contentView.addSubview(cancelButton)
cancelButton.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
cancelButton.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
cancelButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
cancelButton.widthAnchor.constraint(equalToConstant: 300).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Adding a Second CollectionView

In my attempt to add a second CollectionView I have became lost. Here is my future project and I was essentially trying to duplicate that (The reason for the Second collectionView is so that I will have 4 rows, but the top two and bottom two will scroll independently).
Here is the storyboard for reference.
I however get this error here (Second Photo): here
Here is my code for the originally ViewController (WORKING)
Followed by the SecondViewController code, which has caused the app to display the message above.
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet var collectionViewButtons: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
collectionViewButtons.delegate = self
collectionViewButtons.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6 //number of buttons
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ButtonCollectionViewCell
cell.buttonLive.setTitle("Handling a Breakup", for: .normal) //set button title
cell.buttonLive.titleLabel!.font = UIFont(name: "Marker Felt", size: 20)
cell.buttonLive.layer.cornerRadius = 10
cell.buttonLive.clipsToBounds = true
cell.buttonLive.layer.borderWidth = 1.0
cell.buttonLive.layer.borderColor = UIColor.white.cgColor
if indexPath.item == 0 { //first button
cell.buttonLive.backgroundColor = UIColor.darkGray //set button background
}
else if indexPath.item == 1 { //second button
cell.buttonLive.backgroundColor = UIColor.systemGray
cell.buttonLive.setTitle("Good Work", for: .normal)
}
else if indexPath.item == 2 { //3rd button
cell.buttonLive.backgroundColor = UIColor.darkGray
}
else { // for remaining buttons
cell.buttonLive.backgroundColor = UIColor.darkGray
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.item == 0 { // opens any page by clicking button 1
// let vc = storyboard?.instantiateViewController(withIdentifier: "anyVC1") as! ViewController1
// navigationController?.pushViewController(vc, animated: true)
// }
// else if indexPath.item == 1 {
// let vc = storyboard?.instantiateViewController(withIdentifier: "anyVC2") as! ViewController2
// navigationController?.pushViewController(vc, animated: true)
}
// else if indexPath.item == 2 {
// let vc = storyboard?.instantiateViewController(withIdentifier: "anyVC3") as! ViewController3
// navigationController?.pushViewController(vc, animated: true)
}
// else {
// let vc = storyboard?.instantiateViewController(withIdentifier: "anyVC4") as! ViewController4
// navigationController?.pushViewController(vc, animated: true)
}
// }
//}
// You can return any number of buttons by changing return 6 to any required num
SECOND VIEW CONTROLLER:
import UIKit
class SecondViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6 //number of buttons
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let SecondCell = collectionView.dequeueReusableCell(withReuseIdentifier: "SecondCell", for: indexPath) as! ButtonCollectionViewCell
SecondCell.buttonTwo.setTitle("Handling a Breakup", for: .normal) //set button title
SecondCell.buttonLive.titleLabel!.font = UIFont(name: "Marker Felt", size: 20)
SecondCell.buttonTwo.layer.cornerRadius = 10
SecondCell.buttonTwo.clipsToBounds = true
SecondCell.buttonTwo.layer.borderWidth = 1.0
SecondCell.buttonTwo.layer.borderColor = UIColor.white.cgColor
if indexPath.item == 0 { //first button
SecondCell.buttonTwo.backgroundColor = UIColor.darkGray //set button background
}
else if indexPath.item == 1 { //second button
SecondCell.buttonTwo.backgroundColor = UIColor.systemGray
SecondCell.buttonTwo.setTitle("Good Work", for: .normal)
}
else if indexPath.item == 2 { //3rd button
SecondCell.buttonTwo.backgroundColor = UIColor.darkGray
}
else { // for remaining buttons
SecondCell.buttonTwo.backgroundColor = UIColor.darkGray
}
return SecondCell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.item == 0 { // opens any page by clicking button 1
// let vc = storyboard?.instantiateViewController(withIdentifier: "anyVC1") as! ViewController1
// navigationController?.pushViewController(vc, animated: true)
// }
// else if indexPath.item == 1 {
// let vc = storyboard?.instantiateViewController(withIdentifier: "anyVC2") as! ViewController2
// navigationController?.pushViewController(vc, animated: true)
}
// else if indexPath.item == 2 {
// let vc = storyboard?.instantiateViewController(withIdentifier: "anyVC3") as! ViewController3
// navigationController?.pushViewController(vc, animated: true)
}
// else {
// let vc = storyboard?.instantiateViewController(withIdentifier: "anyVC4") as! ViewController4
// navigationController?.pushViewController(vc, animated: true)
}
// }
//}
// You can return any number of buttons by changing return 6 to any required num
Notes:
I have also gone through and done the following to no success:
Changed all "collectionView" writings to say "SecondCollection" because that is what my second collectionView is named.
I have set a Collection IBOutlet for both collectionView.
I have set a separate IBOutlet for both buttons.
If you want two (or more) collection views, you don't want two controllers... you need to check which collection view is requesting data (or being interacted with) and return the appropriate information.
So, your class will look something like this:
class TwoCollectionsViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet var firstCV: UICollectionView!
#IBOutlet var secondCV: UICollectionView!
let firstData: [String] = [
"Btn 1", "Btn 2", "Btn 3", "Btn 4", "Btn 5",
]
let secondData: [String] = [
"Second 1", "Second 2", "Second 3", "Second 4"
]
override func viewDidLoad() {
super.viewDidLoad()
firstCV.dataSource = self
firstCV.delegate = self
secondCV.dataSource = self
secondCV.delegate = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// if it's the First Collection View
if collectionView == firstCV {
return firstData.count
}
// it's not the First Collection View, so it's the Second one
return secondData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// if it's the First Collection View
if collectionView == firstCV {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "firstCell", for: indexPath) as! FirstCollectionViewCell
cell.buttonOne.setTitle(firstData[indexPath.item], for: [])
return cell
}
// it's not the First Collection View, so it's the Second one
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "secondCell", for: indexPath) as! SecondCollectionViewCell
cell.buttonTwo.setTitle(secondData[indexPath.item], for: [])
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// if it's the First Collection View
if collectionView == firstCV {
// do what you want because a cell in the First Collection View was selected
return
}
// it's not the First Collection View, so it's the Second one
// do what you want because a cell in the Second Collection View was selected
}
}

How to implement favorite button in tableView cell?

I need to implement a save button. When a user taps on the button, it has to be filled, by default it's unfilled. But when I run my app some of the buttons in tableView cell are already filled like that
Here's code from TableViewCell
var isFavorite: Bool = false
private let addToFavorites: UIButton = {
let button = UIButton(type: .custom)
button.setImage(UIImage(systemName: "heart"), for: .normal)
button.tintColor = UIColor.white.withAlphaComponent(0.85)
button.contentVerticalAlignment = .fill
button.contentHorizontalAlignment = .fill
return button
}()
override func awakeFromNib() {
super.awakeFromNib()
setFavoriteButtonUI()
addToFavorites.addTarget(self, action: #selector(markFavorite), for: .touchUpInside)
}
#objc func markFavorite() {
setFavoriteButtonImage()
}
private func setFavoriteButtonImage() {
isFavorite = !isFavorite
let imgName = isFavorite ? "heart" : "heart.fill"
let favoriteButtonImage = UIImage(systemName: imgName)
self.addToFavorites.setImage(favoriteButtonImage, for: .normal)
}
private func setFavoriteButtonUI() {
addToFavorites.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(addToFavorites)
addToFavorites.topAnchor.constraint(equalTo: filmImgView.topAnchor, constant: 40).isActive = true
addToFavorites.trailingAnchor.constraint(equalTo: filmImgView.trailingAnchor, constant: -20).isActive = true
addToFavorites.heightAnchor.constraint(equalToConstant: 30).isActive = true
addToFavorites.widthAnchor.constraint(equalToConstant: 40).isActive = true
}
In cellForRowAt indexPath method I added
tableView.reloadRows(at: [indexPath], with: UITableView.RowAnimation.top)
It is a classical problem for iOS beginners.
TableViewCell has a mechanism of reusing object pool.
When some cell with filled heart slides off the screen, the cell enters the reusing object pool.
The new cell appeared on the screen, maybe is created, or pulled from the reusing object pool.
The simple solution is Mark & Config.
Just to maintain the cell status data out the scope of cell, we usually have the status data source on the controller.
#objc func markFavorite() changes the status data source, then table reload data.
in func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell , config the cell heart status.
in Controller:
var selected = [Int]()
func select(index idx: Int){
selected.append(idx)
table.reloadData()
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
// ...
cell.proxy = self
cell.config(willShow: selected.contains(indexPath.row), idx: indexPath.row)
}
in Cell:
var proxy: XXXProxy? // learn sth proxy yourself
var idx: Int?
func config(willShow toShow: Bool, idx index: Int){
idx = index
btn.isHidden = !toShow
}
// add the rest logic yourself

Refreshing or reloading data on just single object inside a collectionViewCell

i'm trying to update just one single object inside my costumViewCell,
i've tried collectionView.reloadItems(at: [IndexPath]), but this method updates my entire cell, which results to a very jittering animations.
here is a sample code of my collectionView cell,
class MyCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var buttonA: UIButton!
#IBOutlet weak var buttonB: UIButton!
var myButtonTitle: String? {
didSet{
if let title = myButtonTitle {
self.buttonA.setTitle(title, for: .normal)
}
}
}
var buttonActionCallBack: (()->()?)
override func awakeFromNib() {
super.awakeFromNib()
self.animation()
buttonA.addTarget(self, action: #selector(buttonACallBack), for: .touchUpInside)
}
#objc fileprivate func buttonACallBack() {
self.buttonActionCallBack?()
}
fileprivate func animation() {
UIView.animate(withDuration: 1.0) {
self.buttonA.transform = CGAffineTransform(translationX: 20, y: 20)
self.buttonB.transform = CGAffineTransform(translationX: 20, y: 20)
}
}
}
here is my DataSource method.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! MyCollectionViewCell
let item = mainList[indexPath.row]
collectionView.reloadItems(at: <#T##[IndexPath]#>)
cell.buttonActionCallBack = {
() in
//Do Stuff and Update Just ButtonA Title
}
return cell
}
cheers.
The jittering animation occurs because of this collectionView.reloadItems(at: [IndexPath]) line written inside cellForItemAt which is really wrong approach because cellForItemAt called many a times leads to infinite loop of reloading IndexPath's. Instead of that, you just reload only that part which is necessary when action occurs.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! MyCollectionViewCell
let item = mainList[indexPath.row]
//collectionView.reloadItems(at: <#T##[IndexPath]#>) #removed
cell.buttonActionCallBack = {
() in
//Do Stuff and Update Just ButtonA Title
collectionView.reloadItems(at: [indexPath]) //Update after the change occurs to see the new UI updates
}
return cell
}

How to get the TextField's text located inside a collectionviewcell?

Im kind of new with Swift, what I am trying to do is to get the text typed by an user in a TextField which is located inside a collection view cell. I have a CollectionViewCell named "PestañaCero" where I created the TextField, this one:
import Foundation
import UIKit
class PestañaCero: UICollectionViewCell
{
let NombreUsuarioTextField: UITextField =
{
let nombre = UITextField()
nombre.borderStyle = UITextBorderStyle.roundedRect
nombre.placeholder = "Nombre de Usuario"
nombre.textAlignment = .center
return nombre
}()
let NumerodeContactoTextField: UITextField =
{
let nombre = UITextField()
nombre.borderStyle = UITextBorderStyle.roundedRect
nombre.placeholder = "Numero de Contacto"
nombre.textAlignment = .center
return nombre
}()
let DireccionOrigenTextField: UITextField =
{
let nombre = UITextField()
nombre.borderStyle = UITextBorderStyle.roundedRect
nombre.placeholder = "Direccion de Origen"
nombre.textAlignment = .center
return nombre
}()
let DireccionDestinoTextField: UITextField =
{
let nombre = UITextField()
nombre.borderStyle = UITextBorderStyle.roundedRect
nombre.placeholder = "Direccion de Destino"
nombre.textAlignment = .center
return nombre
}()
func setupViews()
{
addSubview(NombreUsuarioTextField)
addSubview(NumerodeContactoTextField)
addSubview(DireccionOrigenTextField)
addSubview(DireccionDestinoTextField)
//VERTICAL CONSTRAINT
addConstraintsWithFormat("H:|-16-[v0]-16-|", views: NombreUsuarioTextField)
addConstraintsWithFormat("H:|-16-[v0]-16-|", views: NumerodeContactoTextField)
addConstraintsWithFormat("H:|-16-[v0]-16-|", views: DireccionOrigenTextField)
addConstraintsWithFormat("H:|-16-[v0]-16-|", views: DireccionDestinoTextField)
addConstraintsWithFormat("V:|-100-[v0(30)]-12-[v1(30)]-12-[v2(30)]-12-[v3(30)]", views:
NombreUsuarioTextField,NumerodeContactoTextField, DireccionOrigenTextField ,DireccionDestinoTextField)
}
}
Im trying to print the text when touching in a button created in my cellForItemAt, code which is located in my UICollectionViewController class
#objc func confirmarbutton()
{
print("123")
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
var myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "PestañaCero", for: indexPath)
myCell.backgroundColor = UIColor.black
let nombre = UIButton(frame: CGRect(x: myCell.frame.width/2-100, y: 400, width: 200, height: 25))
nombre.setTitle("Pedir Domicilio", for: .normal)
nombre.backgroundColor = UIColor.orange
nombre.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
nombre.addTarget(self, action: #selector(confirmarbutton) , for: .touchUpInside)
myCell.addSubview(nombre)
}
Any help would be really appreciated, thanks everyone
You can set the delegate of the textField inside the cell to controller when the cell is created, cell.NumerodeContactoTextField.delegate = self and then use the delegate in the controller. However, the problem with this approach is that you will have to do it for all textFields, so the better solution would be create your own delegate, in cell, like this:
protocol CollectionCellTextFieldDelegate: class {
func textDidChanged(_ textField: UITextField)
}
And then add this to your cell:
class PestañaCero: UICollectionViewCell {
weak var textFieldDelegate: CollectionCellTextFieldDelegate?
}
Now in your cell creation in the controller you do:
cell.textFieldDelegate = self
Conform and implement the delegate in the controller:
func textDidChanged(_ textField: UITextField) {
//Here you will get the textField, and you can extract the textFields text
}
This is just an example of how you would approach this situation. you should be able to modify based on your requirement.
A Small Sample of how You would go about doing this with above approach
My Cell Class
import UIKit
protocol CollectionCellTextFieldDelegate: class {
func cellTextFields(_ fields: [UITextField])
}
class Cell: UICollectionViewCell {
#IBOutlet weak var fieldOne: UITextField!
#IBOutlet weak var fieldTwo: UITextField!
#IBOutlet weak var button: UIButton!
weak var textFieldDelegate: CollectionCellTextFieldDelegate?
#IBAction func buttonClicked(_ sender: UIButton) {
guard let textFieldDelegate = textFieldDelegate else { return } //we don't have do anything if not conformed to delegate
//otherwise pass all textFields
textFieldDelegate.cellTextFields([fieldOne, fieldTwo])
}
}
My Controller Class
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, CollectionCellTextFieldDelegate {
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
//register the cell xib
collectionView.register(UINib(nibName: "Cell", bundle: nil), forCellWithReuseIdentifier: "Cell")
}
//MARK:- CollectionView
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
cell.textFieldDelegate = self
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.bounds.width - 20.0, height: 175.0)
}
//you could write this delegate anyway you want, its just for a sample
func cellTextFields(_ fields: [UITextField]) {
//loop over each fields and get the text value
fields.forEach {
debugPrint($0.text ?? "Empty Field")
}
}
}
You will probably have to handle dequeueing of cells as well but for now test this code and modify accordingly.
#objc func confirmarbutton(sender:UIButton)
{
let indexPath = self.collView.indexPathForItem(at: sender.convert(CGPoint.zero, to: self.collView))
let cell = self.collView.cellForItem(at: indexPath!) as! PestañaCero
print(cell.NombreUsuarioTextField.text) // use textfield value like this
print("123")
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
var myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "PestañaCero", for: indexPath)
myCell.backgroundColor = UIColor.black
let nombre = UIButton(frame: CGRect(x: myCell.frame.width/2-100, y: 400, width: 200, height: 25))
nombre.setTitle("Pedir Domicilio", for: .normal)
nombre.backgroundColor = UIColor.orange
nombre.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
nombre.addTarget(self, action: #selector(confirmarbutton(sender:)) , for: .touchUpInside)
myCell.addSubview(nombre)
}
you can access Any row using the indexpath you just need to pass specific indexpath in cellForItem method to get that row so i just pass my sender and find that row to get that textfield value you just replace my code with yours and it will work :)
Here is a simple solution which I personally follow :
First we have should be able to figure it out that at which index/row's button user has clicked so to know that we will set the "indexPath" to button layer like below in * cellForItemAt* method:
nombre.layer.setValue(indexPath, forKey: "indexPath")
then we need change signature of confirmarbutton method like below (as written in answer by #Mahesh Dangar):
#objc func confirmarbutton(sender:UIButton)
Then we need the indexPath in confirmarbutton method so we can get the cell First and then text field to access the value of that text field :
#objc func confirmarbutton(sender:UIButton){
let indexPath = sender.layer.value(forKey: "indexPath") as! IndexPath
let cell = collectionView.cellForItem(at: indexPath) as! PestañaCero
let number = cell.NombreUsuarioTextField.text! // make sure you have value in textfield else you will get runTime error
//below is safer alternative to above line...write one of them
if let isNumberEntered = cell.NombreUsuarioTextField.text{
//condition will be true if text field contains value
}else{
//This block will be executed if text field does not contain value/it is empty. you can show alert something like please enter the number etc.
}
}