How to Save Text Field Value in UIcollectionviewcell

How to save text field value in uicollectionviewcell

Implement in your ViewController the UITextFieldDelegate protocol, specifically the textField:didEndEditing method.

Save your indexPath.row in the textField.tag as you're doing, and set the delegate to the controller, where you can save the value.

This is a very simplistic example:

class MyViewController : UITableViewController {

var texts = [Int:String]()

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier( "Cell" ) as! UITableViewCell

cell.textField.delegate = self
cell.textField.tag = indexPath.row
// restore saved text, if any
if let previousText = texts[indexPath.row] {
cell.textField.text = previousText
}
else {
cell.textField.text = ""
}
// rest of cell initialization
return cell
}

}

extension MyViewController : UITextFieldDelegate {
func textFieldDidEndEditing(textField: UITextField) {
// save the text in the map using the stored row in the tag field
texts[textField.tag] = textField.text
}
}

Save a UITextField into a UICollectionViewCell

Your collection view has a data source where you initially have the model for your cells. In your case it's probably an array. The collection view looks at this data source and returns cells based on the entries contained in this data source.

What I am trying to get at is the following:

In order to show the newly created cell, you'll need to update your data source (I'll call it "the blueprint specification" the collection view adheres to) by adding the new cell's "specification" (I think I'm wording this in a more complex fashion than it actually is). Here is an example (If we assume that your data source is an array):

categoryArray.append(CategoryModel(title: yourTextField.text)) // update the data source

After you make changes to the data source you can tell the collection view to reload it's data:

collectionView.reloadData()

That will "parse" the data source to display the cells anew.

Let me know if anything is unclear.

EDIT

Regarding your comment -- given that you use a UINavigationController -- , you can achieve displaying the title like this:

You are probably using this method to go to your next view controller:

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {

let category = categoriesArray[indexPath.row]
let detailVC = DetailViewController()
detailVC.title = category.title // your title
self.navigationController?.pushViewController(detailVC, animated: true, completion: nil)

}

Alternatively, in your DetailViewController you can do:

class DetailViewController : UIViewController{

var category : Category!

override func viewWillAppear(){
super.viewWillAppear()
self.title = self.category.title

}

}

UITextField values inside UICollectionView cell in swift

You can solve this problem by keeping the values of UITextField in an Array. Whenever you are entering value to UITextField and dismissing keyboard,then save that value to array at the same cell index value in array and when you scroll your collectionView, the textfield value should entered from array and it won't misplace value.

update Label with Textfield inside UICollectionViewCell

There are a couple of things you'll need to do to make this work.

First, you need to preserve context of the clicked cell by storing a "reference" to it. The easiest thing would be to introduce three new variables - section and item. Using these values you'll be able to re-create index path object and reference the cell.

Second, instead of calling dequeueReusableCellWithReuseIdentifier(), you should construct the new Index Path object using the values saved in the previous step and call cellForItemAtIndexPath(_ indexPath: NSIndexPath). This may return a nil if the cell is not in view.

Note: this will set the label only when the user taps Save and the text will most likely be lost if the cell scrolls out of view and then back. In order to preserve this, you should store the entered value in a variable (or refer to the textbox directly) when creating the cell in cellForItemAtIndexPath method.

Modified code example (some of the syntax may be off, I'm doing this from memory):

private let reuseIdentifier: String = "Item"

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UIGestureRecognizerDelegate, UITextFieldDelegate {

override func viewDidLoad() {
super.viewDidLoad()
ItemCollection.delegate = self
}

// Store the clicked item location
private section: Int = 0
private item: Int = 0

func textEnter() {
var text = textField.text

let indexPath = NSIndexPath(forItem: item inSection:section)
let cell = ItemCollection.cellForItemAtIndexPath(indexPath) as! ItemCell

cell.nameLabel.text = text
}

@IBAction func save(sender: AnyObject, cell: UICollectionViewCell) {
textEnter()
}

@IBOutlet weak var textField: UITextField!
@IBOutlet weak var ItemCollection: UICollectionView!

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! ItemCell

cell.backgroundColor = UIColor(red: 0/256, green: 128/256, blue: 255/256, alpha: 0.66)
cell.nameLabel.text = "Test1"
return cell
}

// Need to capture the tapped cell in here
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
section = indexPath.section
item = indexPath.item
}

func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}

}

Editable text filed in UICollectionViewCell blocks didSelectItemAt from being called

  1. Considering you need your text field to be editable.
  2. didSelect will work if cell is touched outside of textfield.
  3. It is not unlikely so if you want to recognize didSelect along with editing, you will need to do the calculation in textField didBeginEditing. A basic hack will be to set index path's values as tag or other property of your textfield, in cellForItemAt (check eg.). You can create a custom text field as well.

Here is update to your cellForItemAt:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LetterCell", for: indexPath) as! LetterCell
cell.singleLetterTextField.text = data[row][column]

cell.singleLetterTextField.tag = indexPath.row//then you can use this tag to form indexPath and with that you can retrieve cell (if it's still visible)

increaseRowColumn()

return cell
}

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

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.

update and get text from uitextfield inside uicollectionciewcell

You should create an XIB for UICollectionviewCell. That will be an easier approach.
Take IBOutlets for buttons and textfield. You have already set them as properties.
In your method you have got the indexpath. So you are very near to the solution. You can get the UICollectionViewCell using the method UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];

Then you can access properties of these cells.

Fetching manually entered data from CollectionView cells and storing it in an array of objects, using Swift

Create something similar to the following:

// create your own delegate type for the cell
protocol MyCellDelegate {
func myTextFieldChanged(_ tf: UITextField) // call when the textfield changes
func myOtherTextFieldChanged(_ tf: UITextField) // call when the other textfield changes
func myToggleChanged(_ sw: UISwitch) // call when the switch changes
}

class MyCell: UICollectionViewCell {
@IBOutlet private var myTextField: UITextField!
@IBOutlet private var myOtherTextField: UITextField!
@IBOutlet private var myToggle: UISwitch!

private var _delegate: MyCellDelegate? // instance of above protocol type, this will generally be your VC

func initialize(withDelegate delegate: MyCellDelegate) {
myTextField.delegate = self
myOtherTextField.delegate = self
self._delegate = delegate // a textfield uses a delegate pattern
self.myToggle.addTarget(self, action: #selector(toggleValueChanged(_:)), for: .valueChanged) // a switch uses this target/selector pattern
}
}

// I like putting delegate implementations in extensions
extension MyCell: UITextFieldDelegate {
// called when a textfield changes
func textFieldDidEndEditing(_ textField: UITextField) {
if textField == myTextField {
_delegate?.myTextFieldChanged(textField) // call the protocol's method to signal the VC of the change
}
if textField == myOtherTextField {
_delegate?.myOtherTextFieldChanged(textField) // call the protocol's method to signal the VC of the change
}
}
}

extension MyCell {
// @objc is required for the target/selector pattern
@objc func toggleValueChanged(_ toggle: UISwitch) {
if toggle == myToggle {
_delegate?.myToggleChanged(toggle)
}
}
}

Then in your VC's cellForItemAt:

let cell=collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SampleCollectionViewCell;
cell.initialize(withDelegate: self)
return cell;

And also in your VC, make it a MyCellDelegate with:

extension SampleViewController: MyCellDelegate{
func myTextFieldChanged(_ tf: UITextField) {
// save new textfield value
}
func myOtherTextFieldChanged(_ tf: UITextField) {
// save new other textfield value
}
func myToggleChanged(_ sw: UISwitch) {
// save new toggle value
}
}

Ideally, you would create a single method that updates your entire form at once, but that really depends on what kind of data you have and what is optional and whatnot, I'll leave that as a challenge. But at least from this you should be able to get your form working and understand what is going on with all the delegate stuff.



Related Topics



Leave a reply



Submit