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

Upvotes: 2

Views: 2083

Answers (3)

kathayatnk
kathayatnk

Reputation: 1015

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.

Upvotes: 1

Ganesh Pawar
Ganesh Pawar

Reputation: 191

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.
    }
}

Upvotes: 0

Mahesh Dangar
Mahesh Dangar

Reputation: 804

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

Upvotes: 1

Related Questions