Adding another cell to tableview when done editing textview inside cell - ios

I would just like to preface this with the fact that I don't use storyboards.
I have a tableview with multiple sections that are filled with tableViewcells I have created programmatically. These custom cells include a textfield with some placeholder text. What I want the user to be able to do is to tap on the textfield, type their entry, and then hit "Enter" to dismiss the keyboard and then create a new cell underneath the cell they just edited. This is very similar to the behaviour that happens in the reminders app.
I'm having a hard time trying to figure out how to access the tableview's data model (an array) and figuring out which section that cell is in, adding the new string to the array, and then adding another dumby cell that has the placeholder text.

First at all you have to create a way to communicate between your cell and view controller.
You can use delegate pattern or callbacks for this.
For example:
final class TextFieldCell: UITableViewCell {
// MARK: - IBOutlets
#IBOutlet weak var textField: UITextField!
// MARK: - Local variables
var callback: ((_ text: String) -> Void)?
// MARK: - Lyfecycle
override func awakeFromNib() {
super.awakeFromNib()
textField.delegate = self
}
}
Also don't forget to call our callback:
extension TextFieldCell: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
callback?(textField.text!)
return true
}
}
Great! Now we send our string from cell to controller!
Example of code for your view controller(simplified version):
class ViewController: UIViewController {
// MARK: - IBOutlets
#IBOutlet weak var tableView: UITableView!
// MARK: - Local variables
var titles = ["Hello", "world"]
}
// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titles.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let textFieldCell = tableView.dequeueReusableCell(withIdentifier: "textFieldCell", for: indexPath) as! TextFieldCell
textFieldCell.textField.placeholder = titles[indexPath.row]
textFieldCell.callback = { [weak self] newTitle in // don't forget about memory leaks
guard let `self` = self else { return }
// calculating index path for new row
let newIndexPath = IndexPath(row: indexPath.row + 1, section: indexPath.section)
// appending a new row in table view
self.titles.append(newTitle)
self.tableView.insertRows(at: [newIndexPath], with: UITableView.RowAnimation.automatic)
}
return textFieldCell
}
}

Related

Correct way to use UITableViewDataSource and Custom UITableViewCell Delegate

I'm working on moving the UITableViewDataSource outside of the UITableViewController. However I have some custom cells that have their own delegates, which then call on the tableView to reload.
I'm not sure what the correct way of handling this is. Here's what I have:
final class MyTableViewController: UITableViewController {
lazy var myTableViewDataSource: MyTableViewDataSource = { MyTableViewDataSource(myProperty: MyProperty) }()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = myTableViewDataSource
}
}
Cell
protocol MyTableViewCellDelegate: AnyObject {
func doSomething(_ cell: MyTableViewCellDelegate, indexPath: IndexPath, text: String)
}
final class MyTableViewCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var packageSizeTextField: UITextField!
weak var delegate: MyTableViewCellDelegate?
var indexPath = IndexPath()
override func awakeFromNib() {
super.awakeFromNib()
}
func configureCell() {
// configureCell...
}
func textFieldDidChangeSelection(_ textField: UITextField) {
print(#function)
delegate?.doSomething(self, indexPath: indexPath, text: textField.text ?? "")
}
}
DataSource
final class MyTableViewDataSource: NSObject, UITableViewDataSource {
var myProperty: MyProperty!
init(myProperty: MyProperty) {
self.myProperty = myProperty
}
// ...
func doSomething(_ cell: MyTableViewCell, indexPath: IndexPath, text: String) {
// ...
tableView.performBatchUpdates({
tableView.reloadRows(at: [indexPath], with: .automatic)
})
// ERROR - tableView doesn't exist
}
}
My question is, how do I gain access to the tableView that this class is providing the source for? Is it as simple as adding a reference to the tableView like this?
var tableView: UITableView
var myProperty: MyProperty!
init(myProperty: MyProperty, tableView: UITableView) {
self.myProperty = myProperty
self.tableView = tableView
}
One option is to have your MyTableViewController conform to your MyTableViewCellDelegate and then set the controller as the delegate in cellForRowAt in your dataSource class.
However, you may be much better off using a closure.
Get rid of your delegate and indexPath properties in your cell, and add a closure property:
final class MyTableViewCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var packageSizeTextField: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
configureCell()
}
func configureCell() {
// configureCell...
packageSizeTextField.delegate = self
}
var changeClosure: ((String, UITableViewCell)->())?
func textFieldDidChangeSelection(_ textField: UITextField) {
print(#function)
changeClosure?(textField.text ?? "", self)
// delegate?.doSomething(self, indexPath: indexPath, text: textField.text ?? "")
}
}
Now, in your dataSource class, set the closure:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "mtvc", for: indexPath) as! MyTableViewCell
c.packageSizeTextField.text = myData[indexPath.row]
c.changeClosure = { [weak self, weak tableView] str, c in
guard let self = self,
let tableView = tableView,
let pth = tableView.indexPath(for: c)
else {
return
}
// update our data
self.myData[pth.row] = str
// do something with the tableView
//tableView.reloadData()
}
return c
}
Note that as you have your code written, the first tap in the textField will not appear to do anything, because textFieldDidChangeSelection will be called immediately.
Edit
Here's a complete example that can be run without any Storyboard connections.
The cell creates a label and a text field, arranging them in a vertical stack view.
Row Zero will have the text field hidden and its label text will be set to the concatenated strings from myData.
The rest of the rows will have the label hidden.
The closure will be called on .editingChanged (instead of textFieldDidChangeSelection) so it is not called when editing begins.
Also implements row deletion for demonstration purposes.
The first row will be reloaded when text is changed in any row's text field, and when a row is deleted.
Cell Class
final class MyTableViewCell: UITableViewCell, UITextFieldDelegate {
var testLabel = UILabel()
var packageSizeTextField = UITextField()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
configureCell()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configureCell()
}
func configureCell() {
// configureCell...
let stack = UIStackView()
stack.axis = .vertical
stack.translatesAutoresizingMaskIntoConstraints = false
testLabel.numberOfLines = 0
testLabel.backgroundColor = .yellow
packageSizeTextField.borderStyle = .roundedRect
stack.addArrangedSubview(testLabel)
stack.addArrangedSubview(packageSizeTextField)
contentView.addSubview(stack)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
stack.topAnchor.constraint(equalTo: g.topAnchor),
stack.leadingAnchor.constraint(equalTo: g.leadingAnchor),
stack.trailingAnchor.constraint(equalTo: g.trailingAnchor),
stack.bottomAnchor.constraint(equalTo: g.bottomAnchor),
])
packageSizeTextField.addTarget(self, action: #selector(textChanged(_:)), for: .editingChanged)
}
var changeClosure: ((String, UITableViewCell)->())?
#objc func textChanged(_ v: UITextField) -> Void {
print(#function)
changeClosure?(v.text ?? "", self)
}
}
TableView Controller Class
class MyTableViewController: UITableViewController {
lazy var myTableViewDataSource: MyTableViewDataSource = {
MyTableViewDataSource()
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
tableView.register(MyTableViewCell.self, forCellReuseIdentifier: "mtvc")
tableView.dataSource = myTableViewDataSource
}
}
TableView DataSource Class
final class MyTableViewDataSource: NSObject, UITableViewDataSource {
var myData: [String] = [
" ",
"One",
"Two",
"Three",
"Four",
"Five",
"Six",
"Seven",
"Eight",
]
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
myData.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return indexPath.row != 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "mtvc", for: indexPath) as! MyTableViewCell
c.testLabel.isHidden = indexPath.row != 0
c.packageSizeTextField.isHidden = indexPath.row == 0
if indexPath.row == 0 {
myData[0] = myData.dropFirst().joined(separator: " : ")
c.testLabel.text = myData[indexPath.row]
} else {
c.packageSizeTextField.text = myData[indexPath.row]
}
c.changeClosure = { [weak self, weak tableView] str, c in
guard let self = self,
let tableView = tableView,
let pth = tableView.indexPath(for: c)
else {
return
}
// update our data
self.myData[pth.row] = str
// do something with the tableView
// such as reload the first row (row Zero)
tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
}
return c
}
}
Edit 2
There is a lot to discuss which goes beyond the scope of your question, but briefly...
First, as a general rule Classes should be as independent as possible.
your Cell should only handle its elements
your Data Source should only manage the data (and, of course, the necessary funds like returning cells, handling Edit commits, etc)
your TableViewController should, as might be expected, control the tableView
If you are only manipulating the data and wanting to reload specific rows, it's not that big of a deal for your DataSource class to get a reference to the tableView.
But, what if you need to do more than that? For example:
You don't want your Cell or DataSource class to act on the button tap and do something like pushing a new controller onto a nav stack.
To use the protocol / delegate pattern, you can "pass a delegate reference" through the classes.
Here's an example (with just minimum code)...
Two protocols - one for text change, one for button tap:
protocol MyTextChangeDelegate: AnyObject {
func cellTextChanged(_ cell: UITableViewCell)
}
protocol MyButtonTapDelegate: AnyObject {
func cellButtonTapped(_ cell: UITableViewCell)
}
The controller class, which conforms to MyButtonTapDelegate:
class TheTableViewController: UITableViewController, MyButtonTapDelegate {
lazy var myTableViewDataSource: TheTableViewDataSource = {
TheTableViewDataSource()
}()
override func viewDidLoad() {
super.viewDidLoad()
// assign custom delegate to dataSource instance
myTableViewDataSource.theButtonTapDelegate = self
tableView.dataSource = myTableViewDataSource
}
// delegate func
func cellButtonTapped(_ cell: UITableViewCell) {
// do something
}
}
The data source class, which conforms to MyTextChangeDelegate and has a reference to MyButtonTapDelegate to "pass to the cell":
final class TheTableViewDataSource: NSObject, UITableViewDataSource, MyTextChangeDelegate {
weak var theButtonTapDelegate: MyButtonTapDelegate?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! theCell
// assign custom delegate to cell instance
c.theTextChangeDelegate = self
c.theButtonTapDelegate = self.theButtonTapDelegate
return c
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func cellTextChanged(_ cell: UITableViewCell) {
// update the data
}
}
and the Cell class, which will call the MyTextChangeDelegate (the data source class) on text change, and the MyButtonTapDelegate (the controller class) when the button is tapped:
final class theCell: UITableViewCell, UITextFieldDelegate {
weak var theTextChangeDelegate: MyTextChangeDelegate?
weak var theButtonTapDelegate: MyButtonTapDelegate?
func textFieldDidChangeSelection(_ textField: UITextField) {
theTextChangeDelegate?.cellTextChanged(self)
}
func buttonTapped() {
theButtonTapDelegate?.cellButtonTapped(self)
}
}
So, having said all that...
Speaking in the abstract can be difficult. For your specific implementation, you may be digging yourself into a hole.
You mention "how to use a containerView / segmented control to switch between controllers" ... It might be better to create a "data manager" class, rather than a "Data Source" class.
Also, with a little searching for Swift Closure vs Delegate you can find a lot of discussion stating that Closures are the preferred approach these days.
I put a project up on GitHub showing the two methods. The functionality is identical --- one approach uses Closures and the other uses Protocol/Delegate pattern. You can take a look and dig through the code (tried to keep it straight-forward) to see which would work better for you.
https://github.com/DonMag/DelegatesAndClosures

how to add label text to tableViewCell

I'm practicing creating an app where I have a label that gets its text from an UITextField when the user presses a button. Now, I added another button and a tableview and I want to be able to "save" the label's text to the table cells with the same mechanism of stopwatch's laps.
So, to be clear, I want the button to transfer the label's text to the table view cells each time I press it.
After your save button, you need to store the texts somewhere and reload the table. (Or insert it with animation)
class ViewController: UIViewController {
#IBOutlet private var textField: UITextField!
#IBOutlet private var tableView: UITableView!
var texts: [String] = [] {
didSet { tableView.reloadData() }
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "SimpleCell")
tableView.dataSource = self
}
#IBAction func saveButtonTapped(_ sender: UIButton) {
guard let newText = textField.text else { return }
self.texts.append(newText)
}
}
And in tableView dataSource methods:
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return texts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SimpleCell", for: indexPath)!
cell.textLabel?.text = texts[indexPath.row]
return cell
}
}

Loop through a uitableviewcell on submit in swift 4

I am trying to access each value of a text field in a prototype cell within a UITableView on Submit. I know I should be doing this in a better way (model) but for now, I just need to access these fields and cannot find a way to do this in Swift 3/4. Would anyone be able to assist?
Code:
import UIKit
import Firebase
class FormTableViewController: UITableViewController {
var formLabels = [String]()
var formPlaceholders = [String]()
override func viewDidLoad() {
super.viewDidLoad()
FirebaseApp.configure()
formLabels = ["Name","Email","Password", "Phone"]
formPlaceholders = ["John Smith","example#email.com","Enter Password", "8585551234"]
tableView.estimatedRowHeight = 30
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return formLabels.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier:
"FormTableCell", for: indexPath)
as! FormTableViewCell
let row = indexPath.row
cell.formLabel.font =
UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)
cell.formLabel.text = formLabels[row]
cell.formTextField.placeholder = formPlaceholders[row]
return cell
}
#IBAction func submitButtonPressed(_ sender: Any) {
// Need to do something with the Name, Email, Phone and Password fields here
}
}
You seem to acknowledge that updating the model directly probably makes sense. So why not do that? Just:
Have model collection for the responses;
Set up delegate for the text field in the cell;
Have cellForRowAt set that delegate; and
Make the table view controller conform to that class.
So, something quick and dirty, set up the cell to hook up editChanged event from the text field and set up protocol to inform the view controller:
protocol FormTableViewCellDelegate: class {
func fieldValueChanged(cell: UITableViewCell, textField: UITextField)
}
class FormTableViewCell: UITableViewCell {
weak var delegate: FormTableViewCellDelegate?
#IBOutlet weak var formLabel: UILabel!
#IBOutlet weak var formTextField: UITextField!
#IBAction func editingChanged(_ sender: UITextField) {
delegate?.fieldValueChanged(cell: self, textField: sender)
}
}
And then have the view controller set up model object and conform to your new protocol:
class FormTableViewController: UITableViewController {
var formLabels = [String]()
var formPlaceholders = [String]()
var values = [String?]()
override func viewDidLoad() {
super.viewDidLoad()
...
formLabels = ["Name","Email","Password", "Phone"]
formPlaceholders = ["John Smith","example#email.com","Enter Password", "8585551234"]
values = [nil, nil, nil, nil]
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FormTableCell", for: indexPath) as! FormTableViewCell
let row = indexPath.row
cell.formLabel.font = .preferredFont(forTextStyle: .headline)
cell.formLabel.text = formLabels[row]
cell.formTextField.placeholder = formPlaceholders[row]
cell.formTextField.text = values[row]
cell.delegate = self // set the delegate, too
return cell
}
#IBAction func submitButtonPressed(_ sender: Any) {
print(#function, values)
}
}
// delegate protocol to update model as text fields change
extension FormTableViewController: FormTableViewCellDelegate {
func fieldValueChanged(cell: UITableViewCell, textField: UITextField) {
guard let indexPath = tableView.indexPath(for: cell) else { return }
values[indexPath.row] = textField.text
}
}
Then that's it, your model is updated as the text fields are updated. Plus this has the advantage that it now supports cell reuse, conforms to MVC patterns, etc.
If you want to just loop through cells, you can create an array of ‘IndexPath’.
let array = (0..<formLabels.count).map { IndexPath(row: $0, section:0) }
After that you can loop over this array and access individual cell using tableview method:- tableView.cellForIndexPath
Hope this helps. (Not on my laptop, so didn’t test the syntax)

Retrieve textfield.text which is inside custom cell

I have tableview which has custom cells.
Each cell has 3 textfields: dayInWeek, startTime, endTime.
In below image, it has 2 rows. But user can click + button to add more rows.
If user click Submit button, I want to loop to every rows, collect 3 textfields data, and store in array or whatever.
Custom TableViewCell:
import UIKit
class RegularScheduleCell: UITableViewCell {
#IBOutlet weak var dayInWeek: UITextField!
#IBOutlet weak var startTime: UITextField!
#IBOutlet weak var endTime: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
And a view controller:
import UIKit
class RegularScheduleVC: UIViewController, UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var numOfRow = 1
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numOfRow
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "RegularScheduleCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! RegularScheduleCell
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == UITableViewCellEditingStyle.delete) {
numOfRow -= 1
self.tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.right)
tableView.reloadData()
}
}
func insertNewRow(_ sender: UIBarButtonItem) {
if numOfRow < 7 {
numOfRow += 1
tableView.reloadData()
}
}
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
tableView.setEditing(editing, animated: animated)
}
}
At this moment, I try to use UITextFieldDelegate
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "RegularScheduleCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! RegularScheduleCell
cell.dayInWeek.delegate = self
cell.startTime.delegate = self
cell.endTime.delegate = self
return cell
and
func textFieldDidEndEditing(_ textField: UITextField) {
allCellsText.append(textField.text!) //allCellsText is an array
print(allCellsText)
}
so that when user finish editing, then add that data to array.
However, this does not satisfy my requirement, because:
on the same cell: can not know if the data is belong to dayOfWeek, or startTime, or endTime
on 2 different cells: can not know if data is belong to, let say, dayOfWeek of 1st cell or dayOfWeek of 2nd cell.
Therefore, How can I loop to all cells, get all 3 text fields data?
Thanks
Method 1:
Make an array of key pairs as-
var arrayOfKeyPairs = [[String:Any]]()
arrayOfKeyPairs.append(["header":"xx",
"value" : "",
“id”: "dsd",
"order" : 0])
We are just replacing the default values with user input values as-
func textFieldDidEndEditing(_ textField: UITextField) {
let center: CGPoint = textField.center
let rootViewPoint: CGPoint = textField.superview!.convert(center, to: tableView)
let indexPath: IndexPath = tableView.indexPathForRow(at: rootViewPoint)! as IndexPath
arrayOfKeyPairs[indexPath.row ]["value"] = textField.text//here you are appending(replacing) data to array
}
On click of submit button, cross check what you received as-
func tapsOnNext(){
self.view.endEditing(true)//for adding last text field value with dismiss keyboard
print(arrayOfKeyPairs)
}
Method 2:
We can get cell data by accessing the cell with particular indexpath as
func tapsOnNext(){
let indexpath = IndexPath(row: 0, section: 0)
let cell = tableView.cellForRow(at: indexPath) as! CustomTableViewCell
print(cell.myTextField.text)
}
You can get text of UITextField with adding target to UITextField
cell.YOUR_TEXTFIELD.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
//EditingChanged is one of the events and will be fired whenever the user changes any character in that UITextField.
After that, you can call your function like this:
func textFieldDidChange(textField: UITextField) {
//your code
}
Don't forget to create class for UITableViewCell and to create IBOutlets of all your UITextField in that custom cell class
You can do it the following way.
First of all you can create an object for example "History"
make its properties like daysInWeek, startTime, endTime.
In your viewDidLoad method you define an array of Objects. Populate the data in the array, or save those objects of "History" that you created in this array.
Set the dataSource of the table view to this array.
in your method cellForRowAtIndexPath you can access the elements of the array you created above.
When you are tapping the plus button, you can create a new object of History, save this object in the array and reload the table view.
If you can share the git repo of this code, i will show how this is being done.
Simple code: On button click get cell data from cell's text field
let indexpath = IndexPath(row: 0, section: 1)
let cell = ItemtableView.cellForRow(at: indexPath) as! CustomTableViewCell
print("text \(cell.myTextField.text)")

How to know which cell in tableview has been selected

I have got a view controller which contains a tableview, this tableview contains dynamic cells done in storyboard. Most cells contain a textfield and in the view controller i need to detect when the textfields have been selected and which cell it was. (I load a popover pointing to the cell, and this must be called from the view controller).
Cell code
import UIKit
class AddNew_Date_Cell: UITableViewCell {
#IBOutlet var Label: UILabel!
#IBOutlet var textField: UITextField!
func loadItem(var data:NSArray) {
Label.text = data[0] as? String
}
}
ViewControllerCode
import UIKit
class AddNewDetailView: UIViewController, UITableViewDelegate, UITableViewDataSource, UIPopoverPresentationControllerDelegate, UITextFieldDelegate {
#IBOutlet var tableView: UITableView!
var items : NSMutableArray!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch(items[indexPath.row][1] as! Int)
{
...
case 2:
var cell:AddNew_Date_Cell = tableView.dequeueReusableCellWithIdentifier("AN_Date_Cell") as! AddNew_Date_Cell
cell.loadItem(items[indexPath.row] as! NSArray, view: self)
cell.textField.delegate = self
return cell
...
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
//Need to know here which cell the textfield is attached to
turn true
}
}
Each time i select a textfield view I'm hitting a breakpoint in "textFieldShouldBeginEditing" however i have no idea which cell its in.
if i select the textfield in a cell "didSelectRowAtIndexPath" is never hit.
How do i find out which cell has been selected.
Thanks
You need to set the interactions from user off while the text field is not been edit, in cellForRowAtIndexPath add this line
cell.textField.userInteractionEnabled = false
In the didSelectRowAtIndexPath you will need to enable again for edition otherwise the user won't be to touche it if they want to and call first responder:
cell.textField.userInteractionEnabled = true
cell.textField.becomeFirstResponder()
once the user finish editing in textFieldShouldEndEditing you disable the interaction again:
cell.textField.userInteractionEnabled = false
This way the cellForRowAtIndexPath will always be called but will be up to you to set the user to use the correct UITextField.
I hope that helps you
You could use a custom cell class with a delegate protocol letting you know when a text field has started editing.
The custom cell would be the delegate of the text field and the view controller would be the delegate of the custom cell.
For a basic example, your cell class could look like the following:
import UIKit
protocol TextFieldCellDelegate {
func textFieldCellDidBeginEditing(cell: TextFieldCell)
}
class TextFieldCell: UITableViewCell {
#IBOutlet weak var textField: UITextField!
var delegate: TextFieldCellDelegate?
}
extension TextFieldCell: UITextFieldDelegate {
func textFieldDidBeginEditing(textField: UITextField) {
delegate?.textFieldCellDidBeginEditing(self)
}
}
And your view controller would implement the method like this:
extension ViewController: TextFieldCellDelegate {
func textFieldCellDidBeginEditing(cell: TextFieldCell) {
println(cell)
// Do something with cell
}
}
If you want to find out the index of your cell write the below code in func textFieldShouldBeginEditing(textField: UITextField)
var index = 0
var position: CGPoint = textField.convertPoint(CGPointZero, toView: self.<your_table_outlet_name>)
if let indexPath = self.<your_table_outlet_name>.indexPathForRowAtPoint(position)
{
index = indexPath.row
}

Resources