I have a UITextField within a Custom UITableViewCell.
I need to segue to a new view controller when the UITextField that sits within the custom cell, is tapped.
Attempted Solutions
I tried performing the segue inside the CustomTableViewCell class using
public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
print("Text Field Tapped")
return false
}
however this didn't work becuase performSegue is a ViewController function. Then I tried
func someAction() {
performSegue(withIdentifier: "identifier", sender: self)
}
in MyViewController class, however that also did not work (not sure why). This is what my two classes look like:
MyViewController (holds UITableView)
import UIKit
class MyViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:SearchTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "cell") as! SearchTableViewCell
return cell
}
}
CustomTableViewCell
import UIKit
class CustomTableViewCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var someTextField: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
self.someTextField.delegate = self
}
}
Any help that you smart people can provide is greatly appreciated :)
something like this should work:
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 44
tableView.rowHeight = UITableViewAutomaticDimension
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TFCell", for: indexPath) as! TFCell
cell.textField.delegate = self
return cell
}
}
extension TableViewController: UITextFieldDelegate {
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
performSegue(withIdentifier: "TFSegue", sender: textField)
return false
}
}
class TFCell: UITableViewCell {
#IBOutlet weak var textField: UITextField!
}
"TFSegue" is a segue from "TableViewController" to the targetviewcontroller created in storyboard. feel free to ask if anything is unclear!
Related
I'm using UITableViewCell custom class
Using Scrollview for horizontal scroll inside the tableviewcell
Write Tableview delegates in UIViewcontroller
Writing scrollview delegate in UITableview cell class
Delegate method not called.
My Code:
class ViewController: UIViewController {
#IBOutlet weak var matchesTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
matchesTableView.delegate = self
matchesTableView.dataSource = self
tableViewSetup()
}
func tableViewSetup() {
matchesTableView.rowHeight = UITableView.automaticDimension
matchesTableView.estimatedRowHeight = self.view.bounds.height
matchesTableView.tableFooterView = UIView()
matchesTableView.separatorStyle = UITableViewCell.SeparatorStyle.none
}
}
// MARK: UITableviewdelegate
extension ViewController : UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return self.view.bounds.height
}
}
UITableView Cell:
import UIKit
class TableViewCell: UITableViewCell, UIScrollViewDelegate {
#IBOutlet weak var bannerScrollView: UIScrollView!
#IBOutlet weak var matchesPageControl: UIPageControl!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code+
self.bannerScrollView.delegate = self
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let pageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width)
matchesPageControl.currentPage = Int(pageNumber)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
I have table view in which cell consist of multiple sections, each section have different number of rows and every row have a text field. When I wrote in it and scroll down and up the data lost or reshuffled. So I am trying to save textfield data into the 2 dimensional array but I can’t solve this problem.
Here is code in Custom cell:
class CustomCell: UITableViewCell {
#IBOutlet weak var userName: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
View controller code:
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let label = UILabel()
label.text = "Header"
return label
}
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 7
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableview.dequeueReusableCell(withIdentifier: CustomCell.identifier, for: indexPath) as! CustomCell
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 80.0
}
}
Your cells cannot be expected to maintain data (the contents of your UITextField) as they get removed from memory once they are outside the visible bounds of your table view.
You have to look into the UITableViewDataSource protocol and store the contents of your cell’s UITextFields in a class which will remain in memory for the duration of your table view’s view controller.
Typically, people use the view controller to be the Data Source as you have done.
Steps are as follows:
In your view controller's initialization, create and prepare a data structure (array / dictionary keyed on IndexPaths) that will contain the contents of the text you need to store
When dequeuing a cell (in your cellForRowAt function), configure the cell with the necessary string from your data structure, if content exists for that particular indexPath.
When the text is changed by the user in the cell, notify your data source of new contents for the cell's index path
Example:
Define the following protocol:
protocol DataSourceUpdateDelegate: class {
func didUpdateDataIn(_ sender: UITableViewCell, with text: String?)
}
Ensure your UITableViewCell declares a delegate variable and uses it:
class MyCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet var myTextField: UITextField!
weak var dataSourceDelegate: DataSourceUpdateDelegate?
func configureCellWithData(_ data: String?, delegate: DataSourceUpdateDelegate?)
{
myTextField.text = data
myTextField.delegate = self
dataSourceDelegate = delegate
}
override func prepareForReuse() {
myTextField.text = ""
super.prepareForReuse()
}
func textFieldDidEndEditing(_ textField: UITextField) {
dataSourceDelegate?.didUpdateDataIn(self, with: textField.text)
}
}
Make sure your View Controller conforms to DataSourceUpdateDelegate and initialize a variable to manage the data:
class MyViewController: UITableViewController, UITableViewDataSource, DataSourceUpdateDelegate {
var data = [IndexPath : String]()
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = MyCell() // Dequeue your cell here instead of instantiating it like this
let cellData = data[indexPath]
cell.configureCellWithData(cellData, delegate: self)
return cell
}
func didUpdateDataIn(_ sender: UITableViewCell, with text: String?) {
data[tableView.indexPath(for: sender)!] = text
}
}
I am working in swift. I have a textfield that is in a tableview cell. I am trying to store the text of each text field in the tableview so they when the user adds or deletes a row, and then the tableview reloads data, that the text fields stay filled in with the appropriate data.
I tried adding a textfielddidendeditting function but for some reason it is not being called.
EDIT:
Here is my code:
tableViewController:
import UIKit
var rowCount = 0
var textArray = [String]()
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
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 = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
return cell
}
#IBAction func addRow(_ sender: Any) {
rowCount = rowCount + 1
textArray.append("")
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
rowCount = rowCount - 1
textArray.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
}
}
}
tableViewCell:
import UIKit
class TableViewCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var textField: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
textField.delegate = self
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if let myIndexPath = tableView.indexPathForSelectedRow {
let newText = textField.text
textArray.insert(newText, at: myIndexPath)
}
return true
}
}
As far as I could suggest (without any code insight given) you could do the following:
Use a callback in your cell, which gets called every time the textfield ends editing:
.
class MyTableViewCell: UITableViewCell {
var textHasChanged: ((String) -> Void)?
...
}
extension MyTableViewCell: UITextFieldDelegate {
// this gets called every time the user ends editing the text field
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
let newValue = textField.text //here you have your value
// now save it to your data source
self.textHasChanged(newValue)
}
}
In a initializer or in awakeFromNib() function (depends on your usage), set the .delegate property of the textfield to self
Now, to have each cell display the value from the datasource and to apply the changed text to your tableView datasource, add the following lines to your UITableViewController:
.
class MyTableViewController: UITableViewController {
var textArray: [String] = ["abc", "def"]
...
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
cell.textField.text = textArray[indexPath.row]
cell.textHasChanged = { (newValue) in
self.textArray[indexPath.row] = newValue
}
return cell
}
Just comment if you have further questions
I am using two view Controllers i.e. StateListVC and PlayVC. In StateListVC, I am using container view with 3 VCs. After tapping on 3rd View Controller, I am going to PlayVC with delegate method. I don't know how to do that. Please help me to resolve this issue.
StateListVC
class StateListVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
if isMoveToAnotherVC
{
let vc = self.storyboard?.instantiateViewController(withIdentifier: "PlaceVC") as! PlaceVC
vc.delegate = self
}
}
}
extension StateListVC: MoveToAnotherVC {
func moving(string: String) {
print(string)
}
}
ThirdVC
protocol MoveToAnotherVC {
func moving(string: String)
}
class PlaceVC: UIViewController {
#IBOutlet weak var tableViewPlace: UITableView!
var delegate: MoveToAnotherVC? = nil
var arrPlace = ["Place1", "Place2"]
override func viewDidLoad() {
super.viewDidLoad()
}
extension PlaceVC: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrPlace.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = arrPlace[indexPath.row]
return cell
}
}
extension PlaceVC: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
isMoveToAnotherVC = true
print(delegate)
guard let delegate = self.delegate else{
print("Delegate not set")
return
}
delegate.moving(string: "test")
}
}
This is hooked up fine:
protocol MoveToAnotherVC: AnyObject {
func moving(string: String)
}
class StateListVC: UIViewController, MoveToAnotherVC {
override func viewDidLoad() {
super.viewDidLoad()
let vc = self.storyboard?.instantiateViewController(withIdentifier: "PlaceVC") as! PlaceVC
vc.delegate = self
}
func moving(string: String) {
print(string)
}
}
class PlaceVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableViewPlace: UITableView!
weak var delegate: MoveToAnotherVC?
var arrPlace = ["Place1", "Place2"]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrPlace.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = arrPlace[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
delegate?.moving(string: "test")
}
}
I made the protocol a class protocol, moved the view controller instantiation into viewDidLoad, removed some (what I consider) extraneous unwrapping, and stripped your protocol/delegate pattern down to the basics. This works. I would plug this into your project and add what you need piece by piece until it breaks because your problem is outside of this. I suspect it may have to do with something you didn't include in your question but this answers your question about how to set up a protocol/delegate pattern.
I am new to Swift and have been struggling with hiding the keyboard when the textField is on a custom tableview cell. I think the problem stems from the textField Reference being in the TableViewCell class, but i can't be sure. I have tried everything and am a little lost.
My code consists of:
TableViewCell:
import UIKit
class TableViewCell: UITableViewCell, UITextFieldDelegate
{
#IBOutlet weak var userText: UITextField!
#IBAction func answers(_ sender: UITextField)
{
}
override func setSelected(_ selected: Bool, animated: Bool)
{
super.setSelected(selected, animated: animated)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.delegate = self
textField.resignFirstResponder()
return true
}
}
and TableViewController:
import UIKit
class TableViewController: UITableViewController, UITextFieldDelegate
{
var textfield = TableViewCell()
override func viewDidLoad()
{
super.viewDidLoad()
let myText = textfield.userText
textField.resignFirstResponder()
myText?.delegate = self
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
return false
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
return cell
}
}
I tried running the textFieldShouldReturn function from both classes but I can't get it to work.
remove UITextFieldDelegate and delegate func from UITableViewCell
then set delegate in UITableViewController->inside
`override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)as! TableViewCell
cell.userText.delegate = self
return cell
}
then you want to add textFieldShouldReturn in UITableViewController and return true try this
Inside your textFieldShouldReturn function insert textField.resignFirstResponder
Or in your viewDidLoad insert tableView.keyboardDismissMode = .onDrag or .interactive so it dismisses when you scroll if you want
Add this function in your tableView cell class because you have declared delegate in tableviewCell's class.
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField. resignFirstResponder()
return false
}