I have a subclass, CustomCell, which inherits from my parent class, CreateEvent. The subclass describes the individual cells for the table view cell, which is on the CreateEvent View controller. In one specific cell, I have a textfield, that is linked to the CustomCell file, but I am having trouble getting the value from that textfield when a user enters into the textfield. I am also having trouble dismissing the keyboard with outside touches and pressing the return key, but I am primarily focused on getting the text from the textfield. I am familiar with doing these functionalities on a normal swift file but because this is a subclass, I'm not sure what to do. What I've tried is to use:
class CustomCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var entranceFeeTextField: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
And:
class CreateEventVC: UIViewController, UITableViewDelegate, UITableViewDataSource, CustomCellDelegate, UITextFieldDelegate {
override func viewDidLoad() {
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: currentCellDescriptor["cellIdentifier"] as! String, for: indexPath) as! CustomCell
cell.entranceFeeTextField.delegate = self
entranceFeeAmount = cell.entranceFeeTextField.text!
}
This code doesn't run and I'm not exactly sure which textfield delegates I need to run in order to be able to get the Text value from the textfield.
You could use the UITextFieldDelegate methods textFieldShouldEndEditing(:) or textFieldShouldReturn(:) to get the results of the textfield.
for example:
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
print("TextField should end editing method called")
let textFromCell = textField.text!
//do whatever you want with the text!
return true;
}
In this code snippet, textField will actually be your instance of entranceFeeTextField. Because somewhere, when that textfield stops editing, it calls self.delegate?.textFieldShouldEndEditing(entranceFeeTextField) and that method's implementation is inside your CreateEventVC.
Returning true will allow the textfield to end editing. This method will only get called when the user wants to stop editing. So you should remove entranceFeeAmount = cell.entranceFeeTextField.text! from your cellForRowAtIndexPath method because that's where you create your cell. At that point a user will not have typed into your textfield, so no use in getting the text from it as soon as it has been made.
All you have to do is implement one of those methods in CreateEventVC.
Here is the full code: (Xcode 8 swift 3)
(View Controller Class)
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate,UITextFieldDelegate
{
#IBOutlet weak var tbl: UITableView!
var cell = TableViewCell()
override func viewDidLoad()
{
super.viewDidLoad()
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
cell = tbl.dequeueReusableCell(withIdentifier: "CELL") as! TableViewCell
cell.configure(text: "", placeholder: "EnterText")
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 1
}
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
print( cell.returnTextOfTextField() )
print(cell.txtField.text)
cell.txtField .resignFirstResponder()
return true
}
}
TableViewCell class (Custom cell):
class TableViewCell: UITableViewCell,UITextFieldDelegate
{
#IBOutlet weak var txtField: UITextField!
override func awakeFromNib()
{
super.awakeFromNib()
// Initialization code
}
public func configure(text: String?, placeholder: String) {
txtField.text = text
txtField.placeholder = placeholder
txtField.accessibilityValue = text
txtField.accessibilityLabel = placeholder
}
func returnTextOfTextField() -> String
{
print(txtField.text)
return txtField.text!
}
override func setSelected(_ selected: Bool, animated: Bool)
{
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
"CELL" is the identifier given to cell in Nib .
This is working code , I get the value from text field and even keyboard is resigned.
var cell = TableViewCell() // customCell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
cell = tbl.dequeueReusableCell(withIdentifier: "CELL") as! TableViewCell
cell.configure(text: "", placeholder: "EnterText")
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 1
}
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
//cell = tbl.dequeueReusableCell(withIdentifier: "CELL") as! TableViewCell
print( cell.returnTextOfTextField() )
print(cell.txtField.text)
cell.txtField .resignFirstResponder()
return true
}
/// Custom cell class
class TableViewCell: UITableViewCell,UITextFieldDelegate
{
#IBOutlet weak var txtField: UITextField!
override func awakeFromNib()
{
super.awakeFromNib()
// Initialization code
}
public func configure(text: String?, placeholder: String) {
txtField.text = text
txtField.placeholder = placeholder
txtField.accessibilityValue = text
txtField.accessibilityLabel = placeholder
}
func returnTextOfTextField() -> String
{
print(txtField.text)
return txtField.text!
}
override func setSelected(_ selected: Bool, animated: Bool)
{
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Related
I want to add a new row in my TableView when the user presses the return key inside the Custom TableViewCell, which includes a TextField. However, I cannot find a way to do so... how do I view the events of the TextField in my TableView so I can add the row?
My TableViewController
class TableViewController: UITableViewController, CustomCellDelegate,
UITextFieldDelegate {
var rowCount = 1
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
// MARK: - Table view data source
...
// Doesn't Do Anything
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let indexPath = IndexPath(row: rowCount-1, section: 1)
tableView.insertRows(at: [indexPath], with: .automatic)
view.endEditing(true)
return true
}
// Also does nothing
func didReturn(cell: AddActivityTableViewCell, string: String?) {
let indexPath = IndexPath(row: rowCount-1, section: 1)
tableView.insertRows(at: [indexPath], with: .automatic)
view.endEditing(true)
rowCount += 1
}
My CustomTableViewCell
protocol CustomCellDelegate: class {
func didReturn(cell: CustomTableViewCell, string: String?)
}
class CustomTableViewCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var textField: UITextField!
weak var delegate: CustomCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
textField.delegate = self
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
public func configureTextField(text: String?, placeholder: String) {
textField.text = text
textField.placeholder = placeholder
textField.accessibilityValue = text
textField.accessibilityLabel = placeholder
}
public func editableTextField(editable: Bool) {
if editable == true {
textField.isEnabled = true
} else {
textField.isEnabled = false
}
}
// This works
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
delegate?.didReturn(cell: self, string: textField.text)
return true
}
}
Thanks!
I think you missed the set delegate in the cell . Please find the code below which works fine for me
ViewController
class TableViewController: UITableViewController, CustomCellDelegate, UITextFieldDelegate {
var rowCount = 1
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rowCount
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : CustomTableViewCell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomTableViewCell
cell.textField.placeholder = "Row \(indexPath.row)"
cell.delegate = self
return cell
}
func didReturn(cell: CustomTableViewCell, string: String?) {
rowCount += 1
let indexPath = IndexPath(row: rowCount-1, section:0)
tableView.beginUpdates()
tableView.insertRows(at: [indexPath], with: .automatic)
tableView.endUpdates()
view.endEditing(true)
}
}
Custom Cell
protocol CustomCellDelegate: class {
func didReturn(cell: CustomTableViewCell, string: String?)
}
class CustomTableViewCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var textField: UITextField!
weak var delegate: CustomCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
textField.delegate = self
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
public func configureTextField(text: String?, placeholder: String) {
textField.text = text
textField.placeholder = placeholder
textField.accessibilityValue = text
textField.accessibilityLabel = placeholder
}
public func editableTextField(editable: Bool) {
if editable == true {
textField.isEnabled = true
} else {
textField.isEnabled = false
}
}
// This works
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
delegate?.didReturn(cell: self, string: textField.text)
return true
}
}
We are moving away from objective c and implementing the new code in Swift. I am facing problems with UITableViewCells. Whenever I add content in the cell and use UITableView to display it, the content of the cell gets cluttered. How do I prevent the "ques" "ans" label from overlapping?
Output when I run the app:
The class controller goes like:
#objc public class ProfileViewController :UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var detailTableView: UITableView! //connected to storyboard
public override func viewDidLoad() {
super.viewDidLoad()
self.detailTableView.delegate = self;
self.detailTableView.dataSource = self;
self.detailTableView.register(UINib(nibName: "DetailTableViewCell", bundle: nil), forCellReuseIdentifier: "DetailTableViewCell")
}
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "DetailTableViewCell", for: indexPath) as! DetailTableViewCell
return cell
}
}
DetailTableViewCell class
#objc public class DetailTableViewCell: UITableViewCell {
public override func awakeFromNib() {
super.awakeFromNib()
}
public override func prepareForReuse() {
super.prepareForReuse()
}
public override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Xib of cell class
storyboard view of controller
I believe there is something very obvious I am missing out because I defined all the constraints accurately. Can I please get some hints?
I have a tableView with custom cell. in my custom cell I have a like button. for like Button I wrote a function to change state from .normal to .selected like this:
FeedViewCell
class FeedViewCell: UITableViewCell {
#IBOutlet weak var likeButton: UIButton!
var likes : Bool {
get {
return UserDefaults.standard.bool(forKey: "likes")
}
set {
UserDefaults.standard.set(newValue, forKey: "likes")
}
}
override func awakeFromNib() {
super.awakeFromNib()
self.likeButton.setImage(UIImage(named: "like-btn-active"), for: .selected)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func likeBtnTouch(_ sender: AnyObject) {
print("press")
// toggle the likes state
self.likes = !self.likeButton.isSelected
// set the likes button accordingly
self.likeButton.isSelected = self.likes
}
}
FeedViewController :
class FeedViewController: UIViewController {
#IBOutlet var feedTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Register Cell Identifier
let feedNib = UINib(nibName: "FeedViewCell", bundle: nil)
self.feedTableView.register(feedNib, forCellReuseIdentifier: "FeedCell")
}
func numberOfSectionsInTableView(_ tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.feeds.count
}
func tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FeedCell", for: indexPath) as! FeedViewCell
return cell
}
}
But my problem is when I tap like button in cell with indexPath.row 0 the state of button in cell with indexPath.row 3 change state too.
where is my mistake?
thanks
You didn't post all your code, but I can tell you that for this to work the #IBAction func likeBtnTouch(_ sender: AnyObject) { } definition must be inside the FeedViewCell class definition to make it unique to a particular instance of the cell.
As a rule of thumb, I normally ensure that all the UI elements inside my cell are populated in cellForRowAtIndexPath when using dequeued cells. Also it should be set from an external source. I.o.w not from a property inside the cell. Dequeuing cells reuse them, and if not setup properly, it might have some leftovers from another cell.
For example, inside cellForRowAtIndexPath:
self.likeButton.isSelected = likeData[indexPath.row]
I have a UITableView (with a Custom class called CellModelAllNames for each row). Each Row has a Label and a button.
My question is: When btn_addRecording (i.e. the '+' button is clicked on any/each of the rows, how do I get the lbl_name.text, the label name shown, and show a pop up in the ViewController itself. I want to get additional information in the pop up and then save all the info (including the lbl_name to a database).
CellModelAllNames for each row layout:
import UIKit
class CellModelAllNames: UITableViewCell {
#IBOutlet weak var lbl_name: UILabel!
#IBOutlet weak var btn_addRecording: UIButton!
#IBAction func btnAction_addRecording(sender: AnyObject) {
println("clicked on button in UITableViewCell")
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func setCell(setBabyName: String) {
self.lbl_name.text = setBabyName
}
}
Here's the code of my ViewController:
import UIKit
class SecondViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tbl_allNames: UITableView!
var arrayOfNames: [Name] = [Name]()
override func viewDidLoad() {
super.viewDidLoad()
self.tbl_allNames.delegate = self
self.tbl_allNames.dataSource = self
self.tbl_allNames.scrollEnabled = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:CellModelAllNames = self.tbl_allNames.dequeueReusableCellWithIdentifier("CellModelAllNames") as! CellModelAllNames
let name = arrayOfNames[indexPath.row]
cell.setCell(name.name)
println("in tableView, cellforRowatIndex, returning new cells")
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayOfNames.count
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
}
You can use standard UIKit methods to get the cell and its data:
func tappedButton(sender : UIButton) {
let point = sender.convertPoint(CGPointZero, toView: self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(point)!
let name = arrayOfNames[indexPath.row]
// do something with name
}
You can add button action in your ViewController
1) In your function cellForRowAtIndexPath assign button's tag as index (ie. indexPath.row)
cell.btn_addRecording.tag = indexPath.row
2) Add target and action for your button :
cell.btn_addRecording.addTarget(self, action: "buttonPressed:", forControlEvents: .TouchUpInside)
3) Add action in ViewControler (ie. save info in database)
func buttonPressed(button: UIButton!)
{
// Add your code here
let name = arrayOfNames[button.tag]
}
the goal is to create new table row every time any other row is tapped. all rows contain UITextField and that is the problem.
main controller:
var subtasksArray = ["one", "two"]
var addRow = 0
class AddEditTaskController: UIViewController, UITableViewDataSource,
UITableViewDelegate, CustomCellDelegate {
func reloadTable() {
subtaskTable.reloadData()
}
#IBOutlet weak var subtaskTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
var tblView = UIView(frame: CGRectZero)
subtaskTable.tableFooterView = tblView
subtaskTable.tableFooterView?.hidden = true
subtaskTable.backgroundColor = UIColor.clearColor()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return subtasksArray.count + addRow
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: SubtaskCell = subtaskTable.dequeueReusableCellWithIdentifier("subtaskCell") as SubtaskCell
cell.delegate = self
return cell
}
custom cell:
protocol CustomCellDelegate {
func reloadTable()
}
class SubtaskCell: UITableViewCell, UITextFieldDelegate {
var delegate: CustomCellDelegate?
#IBOutlet weak var subtaskTextField: UITextField!
var subtasksArray = [String]()
override func awakeFromNib() {
super.awakeFromNib()
subtaskTextField.delegate = self
}
func textFieldDidBeginEditing(textField: UITextField) {
addRow += 1
delegate?.reloadTable()
}
func textFieldDidEndEditing(textField: UITextField) {
subtasksArray.append(subtaskTextField.text)
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
the problem is that if tap any row, a new one is created, but text field is not active.
if i don't use CustomCellDelegate, textfields in cells work fine, but i couldn't find any other way to reload table except cell delegate.