I'm trying to create a tableview where each cell has multiple textfields.
I was able to create a simple list with a textfield but when I press the textfield nothing happens. The keyboard does not popup and feels like the event is being captured by some other view.
I made sure that user interaction is set to enable.
I've tried to push the view to front like you can see in the comment.
What am I missing?
My table is called MainTable and
my InputviewCell is just a normal cell (xib) with a textfield named txtFieldTemp
import Foundation
import UIKit
class PrioritiesGridViewController: UIViewController,UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate
{
#IBOutlet var mainTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
mainTable.delegate = self
mainTable.dataSource = self
//mainTable.allowsSelection = false
}
let data = AuxClass()
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 62
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.everything().count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = Bundle.main.loadNibNamed("InputTextViewCell", owner: self, options: nil)?.first as! InputTextViewCell
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.txtFieldTemp.delegate = self
cell.txtFieldTemp.text = "test"
cell.txtFieldTemp.allowsEditingTextAttributes = true
//self.view.bringSubview(toFront: cell.txtFieldTemp)
return cell;
}
override var canBecomeFirstResponder: Bool { return true}
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
return true
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1;
}
func textFieldDidBeginEditing(_ textField: UITextField) {
self.isEditing = true
}
}
class AuxClass
{
var objectsArray = [Objects(
field1: "teste",
field2: 3),
Objects(
field1: "teste1",
field2: 4)
]
struct Objects {
var field1: String!
var field2: Int
}
func everything() -> [Objects]
{
return objectsArray
}
}
Try with cell.txtFieldTemp.becomeFirstResponder(). Looking at the apple documentation I think it will force it to be the first responder.
Related
I have a Table View that is working fine. However, when I try to implement a UISearchBar and display filtered data, nothing gets filtered. This is my View Controller:
import UIKit
class MealPlanViewController: UIViewController, UISearchBarDelegate {
private var model = MealPlanModel()
private var mealPlan = [MealPlan]()
var filteredData: [MealPlan]!
#IBOutlet weak var topBarStackView: UIStackView!
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
model.delegate = self
searchBar.delegate = self
filteredData = mealPlan
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filteredData = []
if searchText == "" {
filteredData = mealPlan
}
else {
for item in mealPlan {
if ((item.title?.lowercased().contains(searchText.lowercased())) != nil) {
filteredData.append(item)
}
}
}
self.tableView.reloadData()
}
}
extension MealPlanViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MealPlanCell", for: indexPath) as! MealPlanCell
let filteredMealPlaninTable = filteredData[indexPath.row]
cell.displayMealPlan(filteredMealPlaninTable)
return cell
}
}
extension MealPlanViewController: MealPlanProtocol {
func mealPlansRetrieved(mealPlans: [MealPlan]) {
self.filteredData = mealPlans
tableView.reloadData()
}
}
A couple of notes:
When I run a print(self.filteredData) in my `func mealPlansRetrieved', the console returns all of my data as if it wasn't filtered, but
As soon as I start typing in the search bar, the table view doesn't return any cells, which seems contradictory to the above
For reference, this is the code before filtering that did work:
extension MealPlanViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return mealPlan.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MealPlanCell", for: indexPath) as! MealPlanCell
let mealPlanInTable = mealPlan[indexPath.row]
cell.displayMealPlan(mealPlanInTable)
return cell
}
}
extension MealPlanViewController: MealPlanProtocol {
func mealPlansRetrieved(mealPlans: [MealPlan]) {
self.mealPlan = mealPlans
tableView.reloadData()
}
}
Any help/guidance is much appreciated!
Contains returns boolean so this will never fail: item.title?.lowercased().contains(searchText.lowercased())) != nil
To make check work you can simply remove "!= nil".
Im not sure from where you are calling mealPlansRetrieved, but you might want to keep the line "self.mealPlan = mealPlans" instead of "self.filteredData = mealPlans".
I need to call function deleteButtonShowHide, which is in TeamsCell, from TeamsVC, when plusBtnTapped. I am trying to figure it out with protocol TeamsVCDelegate, but it doesn't work( It works vice versa for me. But I do not know how to implement something like cell.teamsCellDelegate = self
TeamsCell
import UIKit
protocol TeamsCellDelegate {
func deleteCell()
}
class TeamsCell: UITableViewCell {
#IBOutlet weak var teamNameLbl: UILabel!
#IBOutlet weak var deleteButton: UIButton!
var teamsCellDelegate: TeamsCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
}
func updateCell(team: team) {
teamNameLbl.text = team.name
}
#IBAction func deleteButtonTapped(_ sender: Any) {
debugPrint("delet tapped")
//deleteButtonShowHide()
findAndDeleteTeam()
teamsCellDelegate?.deleteCell()
}
func findAndDeleteTeam() {
for i in 0...teams.count - 1 {
if teams[i].name == teamNameLbl.text {
teams.remove(at: i)
break
}
}
}
func deleteButtonShowHide(){
if teams.count < 3 {deleteButton.isHidden = true}
if teams.count > 2 {deleteButton.isHidden = false}
}
}
extension TeamsCell: TeamsVCDelegate {
func deleteButtonSH() {
debugPrint("XXX")
deleteButtonShowHide()
}
}
TeamsVC
import UIKit
protocol TeamsVCDelegate {
func deleteButtonSH()
}
class TeamsVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var plusBtn: UIButton!
#IBOutlet weak var teamsTable: UITableView!
var teamsVCDelegate: TeamsVCDelegate?
override func viewDidLoad() {
super.viewDidLoad()
teamsTable.delegate = self
teamsTable.dataSource = self
teamsTable.rowHeight = 55
teamsTable.isScrollEnabled = false
teamsTable.backgroundColor = nil
teamsTable.separatorStyle = .none
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return teams.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "TeamsCell") as? TeamsCell {
cell.updateCell(team: teams[indexPath.row])
cell.teamsCellDelegate = self
return cell
}
return UITableViewCell()
}
#IBAction func plusBtnTapped(_ sender: Any) {
plusBtnHide()
addTeam()
teamsTable.reloadData()
teamsVCDelegate?.deleteButtonSH()
print(teams)
}
func plusBtnShow() {
if teams.count < 5 {plusBtn.isHidden = false}
}
func plusBtnHide() {
if teams.count == 4 { plusBtn.isHidden = true}
}
}
extension TeamsVC: TeamsCellDelegate {
func deleteCell() {
self.teamsTable.reloadData()
self.plusBtnShow()
}
}
You could call deleteButtonShowHide function when you are loading/setting up a cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "TeamsCell") as? TeamsCell {
cell.updateCell(team: teams[indexPath.row])
cell.deleteButtonShowHide() // <-- HERE
cell.teamsCellDelegate = self
return cell
}
return UITableViewCell()
}
By the way, your cell should not contain such logic in the first place. It should depend on some data model object which then should be used to setup your cell correctly (show/hide UI elements, etc.).
You could simplify by setting the button show/hide when computing the number of row.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if teams.count < 5 {plusBtn.isHidden = false}
if teams.count == 4 { plusBtn.isHidden = true}
return teams.count
}
And set the delebutton visibility when creating the cell :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "TeamsCell") as? TeamsCell {
cell.updateCell(team: teams[indexPath.row])
// cell.teamsCellDelegate = self
cell.deleteButton.isHidden = (teams.count < 3)
return cell
}
return UITableViewCell()
}
So no need for delegate and cell does not have to know about the model (teams)
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)
I want to pass the data A view to B view, it can build and show data in A view, but after I selected the cell, it crashed. And it shows the problem on the code.
vcTwo.selectedzones.zones = [selectedCity]
fatal error: unexpectedly found nil while unwrapping an Optional value (lldb)
my code
The struct mode:
struct Location {
var city: String!
var zones = [String]()
}
var city = ["KHT", "TPAP", "TNNY"]
let kh = Location.init(city: "KHT", zones: ["sami", "zami", "zomi", "komi", "shini"])
let tpa = Location.init(city: "TPAP", zones: ["mid", "east", "anci", "zochi"])
let tnn = Location.init(city: "TNNY", zones: ["TN1","TN2", "TN3", "TN4", "TN5"])
Here is the A viewController code:
import UIKit
class FirstViewTableViewController: UITableViewController {
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 city.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! FirstCell
cell.firstLabel.text = city[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCity = city[indexPath.row]
let vcTwo = self.storyboard?.instantiateViewController(withIdentifier: "secondVC") as! secondViewController
vcTwo.selectedzones.zones = [selectedCity]
self.navigationController?.pushViewController(vcTwo, animated: true)
}
}
The B viewController:
import UIKit
class secondViewController: UITableViewController {
var selectedzones: Location!
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return selectedzones.zones.count
}
Is there any part wrong in the func didSelectRowAt indexPath?
here is the problem
vcTwo.selectedzones.zones = [selectedCity]
you need to init the selectedZone in first Controller
let kh = Location.init(city: "KHT", zones: ["sami", "zami", "zomi", "komi", "shini"])
vcTwo.selectedzones = kh
Try this
class secondViewController: UITableViewController {
var selectedzones = Location()
}
you are try to accesss zones but you have not allocate Location so it will crash.
Hope it will help you
Instead of Optional ! use Optional ?
class secondViewController: UITableViewController {
var selectedzones: Location?
NOT
class secondViewController: UITableViewController {
var selectedzones: Location!
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
}
}