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
- Considering you need your text field to be editable.
didSelect
will work if cell is touched outside of textfield.- It is not unlikely so if you want to recognize
didSelect
along with editing, you will need to do the calculation in textFielddidBeginEditing
. A basic hack will be to set index path's values as tag or other property of yourtextfield
, incellForItemAt
(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
Swift 4 "Extra Argument in Call" Rxswift
Proper Way of Editing a Cocoapod Library
F# Asynchronous Http Request - Parse JSON Response
Using Nsdate to Get Date for Easter
Reduce Float Precision Using Regexp in Swift
How to Change an Inout Parameter from Within a Escaping Closure
Swift Unsafemutablepointer & Unsafemutablepointer<Unsafepointer<Sometype>>
Passing Data Between View Controllers (Swift)
Firebase Swift 3 Get List of Child in a Array
Covert Realm List to Realm Result
Core Data Appears to Lose Data After Xcode Upgrade
How to Pass a Completion Block to Another Class in Swift
Print Not Working in Swift 3 Extensions
How to Get The Nearest Int Floored from a Sqrt of an Int
Swiftui Published Updates Not Refreshing
Custom Markers Disappear on Zoomin The Map and Appear on Zoomout The Map with Clustering
How to Force Sktextureatlas Created from a Dictionary to Not Modify Textures Size