I am attempting to create a very simple Table View containing images and labels. I am attempting to customize/style what the inside of each cell looks like (programattically), therefore I have created a class called "BarCell" in order to program how each cell should look. However, when I try to follow this format, I am getting the following error :
Could not cast value of type 'UITableViewCell' (0x10bda7038) to 'ProjectName.BarCell' (0x105332c38).
What does this error mean? I have declared my class of BarCell as a type of UITableViewCell. Therefore, how am I getting this error? Very new to swift, so any tips advise that could point me to the right direction will be extremely helpful. Here is my complete code
//
// HomeController.swift
// Covered
//
// Created by name on 1/3/18.
// Copyright © 2018 name. All rights reserved.
//
import UIKit
import Firebase
class HomeController: UIViewController, UITableViewDelegate, UITableViewDataSource{
let bars = ["one", "two", "three"]
let tableView: UITableView = {
let tv = UITableView()
tv.separatorStyle = .none
tv.allowsSelection = true
return tv
}()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return bars.count
}
var barImage: UIImage?
var barCover: String?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! BarCell
cell.imageView?.image = UIImage(named: bars[indexPath.row])
cell.textLabel?.text = bars[indexPath.item]
barImage = cell.imageView?.image
barCover = cell.textLabel?.text
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 160
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func viewDidLoad() {
super.viewDidLoad()
setUpNavigationBar()
setUpTableView()
//user is not logged in
if Auth.auth().currentUser?.uid == nil {
perform(#selector(handleSignOut), with: nil, afterDelay: 0)
}
}
func setUpNavigationBar(){
navigationItem.title = "Covered"
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Sign Out", style: .plain, target: self, action: #selector(handleSignOut))
navigationItem.leftBarButtonItem?.tintColor = .black
}
func setUpTableView(){
tableView.dataSource = self
tableView.delegate = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
view.addSubview(tableView)
_ = tableView.anchor(view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, topConstant: 0, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: view.frame.width, heightConstant: view.frame.height)
}
#objc func handleSignOut(){
do{
try Auth.auth().signOut()
} catch let logOutError{
print("Here is the log out error: /n")
print(logOutError)
}
//change state of user default
UserDefaults.standard.setIsLoggedIn(value: false)
//present login controller
let loginController = LoginController()
present(loginController, animated: true, completion: nil)
}
}
class BarCell: UITableViewCell{
let cellView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.dropShadow()
return view
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?){
super.init(style: style, reuseIdentifier: reuseIdentifier)
setup()
}
func setup(){
addSubview(cellView)
_ = cellView.anchor(topAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, topConstant: 4, leftConstant: 8, bottomConstant: 4, rightConstant: 8)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
My error is coming up at line 32, which is :
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! BarCell
Any ideas why this is happening? My code runs well when i cast this line as! UITableViewCell directly. However, I am trying to do this in order to set the appearance for each cell. Thanks in advance for any advice.
The problem is that the lines:
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
and
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! BarCell
don't match.
If you really want the cell to have a type of BarCell then register a BarCell instead of s UITableViewCell.
tableView.register(BarCell.self, forCellReuseIdentifier: "cell")
Related
I’m having a problem with embedding tableView inside a UIView. I tell you what my goal is:
My goal is to have a little tableView with 3 rows (like here - Link), now I’ve made a custom UIView with a tableView inside but for some reason it doesn’t load my data + doesn’t show anything. I must be honest I’m not sure if it is the right way of doing that, cause maybe there’s a better way of doing that, so I hope you guys can help me out.
Here's my code:
import Foundation
import UIKit
class TypeTableView: UIView, UITableViewDataSource, UITableViewDelegate {
var typesData = ["Videos","Images","Posts"]
var tableView = UITableView()
// let screenHeight = UIScreen.main.bounds.height
// let screenWidth = UIScreen.main.bounds.width
//
override init(frame: CGRect){
super.init(frame: frame)
self.backgroundColor = .red
setup()
tableView.delegate = self
tableView.dataSource = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func setup() {
tableView = UITableView(frame: CGRect(x: 0, y: 0, width: 1000, height: 100),style: .grouped)
tableView.backgroundColor = .red
tableView.layer.cornerRadius = 10
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
self.addSubview(tableView)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return typesData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath as IndexPath)
cell.textLabel?.text = typesData[indexPath.row]
return cell
}
}
Here's the custom cell of the tableView(the tableView is inside the customCell):
import UIKit
class ByType: UITableViewCell {
// var typesData = ["Videos","Images","Posts"]
#IBOutlet var groupedTableView: TypeTableView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
// self.preservesSuperviewLayoutMargins = false
self.separatorInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)
self.layoutMargins = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)
groupedTableView.layer.cornerRadius = 5
groupedTableView.layer.masksToBounds = true;
groupedTableView.backgroundColor = .red
groupedTableView.layer.borderWidth = 0.5
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
I'm attempting to use a custom UITableViewCell that I created programmatically in a UITableView.
The UITableViewCell class:
class PopulatedCellStyleTableViewCell: UITableViewCell {
private let userDisplayName: UILabel = {
let lbl = UILabel()
lbl.font = UIFont(name: "Baskerville-Bold", size: 16)
lbl.textColor = .purple
lbl.textAlignment = .left
lbl.backgroundColor = UIColor(displayP3Red: 1, green: 1, blue: 1, alpha: 0.5)
return lbl
}()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(userDisplayName)
//from a UIKit extension that allows me to easily add anchors
userDisplayName.anchor(top: self.topAnchor, left: self.leftAnchor, bottom: self.bottomAnchor, right: self.rightAnchor, paddingTop: 5, paddingLeft: 0, paddingBottom: 5, paddingRight: 0, width: self.frame.width, height: self.frame.height, enableInsets: false)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And the UITableViewController class:
class LoveListController: UITableViewController {
var loveList: [String]!
let populatedCell = "populatedCell"
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(PopulatedCellStyleTableViewCell.self, forCellReuseIdentifier: populatedCell)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return loveList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: populatedCell, for: indexPath)
// Configure the cell...
cell.textLabel?.text = loveList[indexPath.row]
return cell
}
}
Upon inspecting the debug view hierarchy, the labels load in, but the actual text is rendered in a UITableViewCellContentView instead of in my UILabel, and I have no idea why.
If this is easily solvable or has previously been discussed, please point me in the right direction and/or teach me how to discover the solution myself!
You need to cast the cell and use your custom userDisplayName not textLabel
let cell = tableView.dequeueReusableCell(withIdentifier: populatedCell, for: indexPath) as! PopulatedCellStyleTableViewCell
cell.userDisplayName.text = loveList[indexPath.row]
plus it's better to add the label to contentView and create the constraints with it
self.contentView.addSubview(userDisplayName)
self.userDisplayName.anchor(top: self.contentView.topAnchor .....
I have a searchController getting data from Google Places API. I am using a UITableView to populate the cells, however, they are not being populated (I assume due to the above error)
I have all the delegates set up
TableView is reloading data
The request is being printed in the console as expected
From what I have read on S/OF this could be something to do with the registration of the cell - see below code
TableView
func configureTableView() {
tableView.delegate = self
tableView.dataSource = self
view.addSubview(tableView)
tableView.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor,
paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0,
width: 0, height: 0)
tableView.tableFooterView = UIView()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: recentSearchesCellIdentifier)
tableView.register(ResultsCell.self, forCellReuseIdentifier: searchResultsCellIdentifier)
tableView.keyboardDismissMode = .interactive
tableView.backgroundColor = UIColor(red: 242/255, green: 242/255, blue: 243/255, alpha: 1)
}
CellForItemAtIndexPath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let recentSearchesCell = tableView.dequeueReusableCell(withIdentifier: recentSearchesCellIdentifier, for: indexPath)
let searchResultsCell = tableView.dequeueReusableCell(withIdentifier: searchResultsCellIdentifier, for: indexPath) as! ResultsCell
switch sections[indexPath.section].section {
case .RecentSearches:
recentSearchesCell.selectionStyle = .none
let recentSearches = recentResults[indexPath.row]
recentSearchesCell.textLabel?.text = recentSearches
recentSearchesCell.detailTextLabel?.text = recentSearches
return recentSearchesCell
case .SearchResults:
searchResultsCell.selectionStyle = .none
let result = searchResults[indexPath.row]
searchResultsCell.result = result
return searchResultsCell
}
}
Cell
class ResultsCell: UITableViewCell {
var result: GMSAutocompletePrediction? {
didSet {
self.textLabel?.attributedText = result?.attributedPrimaryText
self.detailTextLabel?.attributedText = result?.attributedSecondaryText
}
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
I made a UITextfield to receive the data from user.
I want to convert a value from UITextField to UILabel.
I did it in simple UIView, which has only two object, UITextField and UILabel.
This is the code that works.
class ViewController: UIViewController, UITextFieldDelegate {
let inputNumber = UITextField(frame: CGRect(x: 150.0, y: 100.0, width: 200.0, height: 50.0))
let outputNumber = UILabel(frame: CGRect(x: 150.0, y: 200.0, width: 200.0, height: 50.0))
let toolBarKeyBoard = UIToolbar()
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: #selector(donePressed))
var result : String!
override func viewDidLoad() {
super.viewDidLoad()
calculatePrice()
}
func calculatePrice () {
priceInputLabel.keyboardType = .numberPad
priceInputLabel.clearButtonMode = .whileEditing
self.view.addSubview(priceInputLabel)
toolBarKeyBoard.sizeToFit()
toolBarKeyBoard.setItems([flexibleSpace, doneButton], animated: false)
priceInputLabel.inputAccessoryView = toolBarKeyBoard
}
#objc func donePressed() {
view.endEditing(true)
result = inputNumber.text!
let convertedNumber = (result as NSString).doubleValue
if Int(inputNumber.text!) == nil {
outputNumber.text = String("Nil")
} else {
outputNumber.text = String(Int(convertedNumber * 0.85))
}
}
}
But in other case, down below, the problem is UITextField and UILabel are in the UITableViewCell as subviews.
I made a 3 swift files. 2 files are UITableViewCell subclasses, and 1 file is a UITableView class.
1. FruitTableViewCell : UITableViewCell subclass
class FruitTableViewCell: UITableViewCell, UITextFieldDelegate {
var fruitsTextField = UITextField()
let toolBarKeyBoard = UIToolbar()
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: #selector(donePressed))
var result : String!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(fruitsTextField)
}
override func layoutSubviews() {
super.layoutSubviews()
fruitsTextField.frame = CGRect(x: 250, y: 7.5, width: 100, height: 30)
fruitsTextField.textColor = UIColor(red: CGFloat(242/255.0), green: CGFloat(56/255.0), blue: CGFloat(90/255.0), alpha: 1.0)
fruitsTextField.keyboardType = .numberPad
fruitsTextField.clearButtonMode = .whileEditing
toolBarKeyBoard.sizeToFit()
fruitsTextField.inputAccessoryView = toolBarKeyBoard
toolBarKeyBoard.setItems([flexibleSpace, doneButton], animated: false)
}
#objc func donePressed() {
fruitTextField.endEditing(true)
}
}
2. AnotherFruitTableViewCell : UITableViewCell subclass
class AnotherFruitTableViewCell: UITableViewCell {
var fruitsTextLabel = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(fruitsTextLabel)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
fruitsTextLabel.backgroundColor = UIColor.brown
fruitsTextLabel.frame = CGRect(x: 250.0, y: 7.5, width: 100.0, height: 30.0)
}
}
3. TableViewController : UITableViewController class
class TableViewController: UITableViewController, UITextFieldDelegate {
let fruitsComponents: [String] = ["Apple", "Banana", "Grape", "Pear"]
let cellReuseidentifier = "cell"
let anotherCellReuseidentifier = "anotherCell"
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(FruitTableViewCell.self, forCellReuseIdentifier: cellReuseidentifier)
tableView.register(AnotherFruitTableViewCell.self, forCellReuseIdentifier: anotherCellReuseidentifier)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fruitsComponents.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseidentifier, for: indexPath) as! FruitTableViewCell
cell.textLabel?.text = fruitsComponents[indexPath.row]
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: anotherCellReuseidentifier, for: indexPath) as! AnotherFruitTableViewCell
cell.textLabel?.text = fruitsComponents[indexPath.row]
return cell
}
}
}
The fruitsTextField and fruitsTextLabel is not in the same class like in the first example code.
So, I cannot call both instances and calculate a value in ViewController class. Of course, cannot return a calculated value.
And, I'm not sure I can return after touching a done button to get a value from UITextField to UILabel, because the cells which is the super view of subview(UITextField and UILabel) are reproduced. I'm confusing touching a done button occurs dequeueing cells again.
How can I return a value from UITextField to UILabel in UITableViewCell?
Thanks!
If I understand correctly you want to change some parameter of one cell based on action in another cell (actually the fact that these cells are of different classes is not important to that matter). ViewController will be in that case intermediary, so you need to make cells to communicate with ViewController. For communication between two objects one usually uses delegate or closure pattern.
In that case I would use closure. So, when you instantiate the cell with TextField ViewController tells Cell what to do when Done is pressed. To achieve that:
add var didEntered: ((_ text: String)->())? to FruitTableViewCell
add didEntered?(fruitsTextField.text ?? "") to #objc func donePressed()
add (the code updates the second row based on textfield value - just for a example)
cell.didEntered = {text in
self.fruitsComponents[1] = text
self.tableView.reloadRows(at: [IndexPath(row: 1, section: 0)], with: UITableViewRowAnimation.none)}
to override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
On the way I corrected some mistakes to make it work, so the complete code is below.
Upd 1
import UIKit
class ViewController: UITableViewController, UITextFieldDelegate {
var fruitsComponents: [String] = ["Apple", "Banana", "Grape", "Pear"]
var fruitsLabels: [String] = ["", "", "", ""]
let cellReuseidentifier = "cell"
let anotherCellReuseidentifier = "anotherCell"
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(FruitTableViewCell.self, forCellReuseIdentifier: cellReuseidentifier)
tableView.register(AnotherFruitTableViewCell.self, forCellReuseIdentifier: anotherCellReuseidentifier)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fruitsComponents.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseidentifier, for: indexPath) as! FruitTableViewCell
cell.textLabel?.text = fruitsComponents[indexPath.row]
cell.didEntered = {text in
self.fruitsLabels = Array(repeating: text, count: 4)
self.tableView.reloadData()
}
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: anotherCellReuseidentifier, for: indexPath) as! AnotherFruitTableViewCell
cell.textLabel?.text = fruitsComponents[indexPath.row]
cell.fruitsTextLabel.text = fruitsLabels[indexPath.row]
return cell
}
}
}
class FruitTableViewCell: UITableViewCell, UITextFieldDelegate {
var fruitsTextField = UITextField()
let toolBarKeyBoard = UIToolbar()
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
var result : String!
var didEntered: ((_ text: String)->())?
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(fruitsTextField)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
fruitsTextField.frame = CGRect(x: 250, y: 7.5, width: 100, height: 30)
fruitsTextField.backgroundColor = .yellow
fruitsTextField.textColor = UIColor(red: CGFloat(242/255.0), green: CGFloat(56/255.0), blue: CGFloat(90/255.0), alpha: 1.0)
fruitsTextField.keyboardType = .numberPad
fruitsTextField.clearButtonMode = .whileEditing
toolBarKeyBoard.sizeToFit()
fruitsTextField.inputAccessoryView = toolBarKeyBoard
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(donePressed))
toolBarKeyBoard.setItems([flexibleSpace, doneButton], animated: false)
}
#objc func donePressed() {
fruitsTextField.endEditing(true)
didEntered?(fruitsTextField.text ?? "")
}
}
class AnotherFruitTableViewCell: UITableViewCell {
var fruitsTextLabel = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(fruitsTextLabel)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
fruitsTextLabel.backgroundColor = UIColor.brown
fruitsTextLabel.frame = CGRect(x: 250.0, y: 7.5, width: 100.0, height: 30.0)
}
}
You can use UITextField delegate methods to achieve this in your TableViewController.
In your TableViewController cellForRowAt method:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseidentifier, for: indexPath) as! FruitTableViewCell
cell.textLabel?.text = fruitsComponents[indexPath.row] //"I am confused why it is here"
cell.fruitsTextField.delegate = self
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: anotherCellReuseidentifier, for: indexPath) as! AnotherFruitTableViewCell
cell.textLabel?.text = fruitsComponents[indexPath.row]
return cell
}
}
Now add UITextField Delegate methods:
extension TableViewController : UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
print(textField.text!)
let cell = self.tableView.cellForRow(at: IndexPath(row: 1, section: 0)) as! AnotherFruitTableViewCell
cell.textLabel.text = textField.text!
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.endEditing(true)
return true
}
}
Hope this helps.
I have a tableview (SettingsViewController) which I use as a user info view where user info is displayed (name, email, phone #, etc). This is similar to the standard iOS contact page.
Each cell has a textfield which is stretched accross the size of the cell, so that once in "edit" mode the user can update his/her info.
I also have a custom Cell (SettingsCell) which is where i setup the cell with the textfield etc.
SettingsViewController (excluded a lot of tabelview setup code):
class SettingsViewController: UITableViewController{
let cellId = "cellId"
var apiController: APIController?
var firstName: String?
var lastName: String?
var email: String?
var phoneNumber: String?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .mainWhite()
tableView = UITableView(frame: CGRect.zero, style: .grouped)
tableView.register(SettingsCell.self, forCellReuseIdentifier: cellId)
setupNavigation()
}
fileprivate func setupNavigation() {
editButtonItem.action = #selector(showEditing)
editButtonItem.title = "Edit"
editButtonItem.tintColor = .mainWhite()
self.navigationItem.rightBarButtonItem = editButtonItem
}
#objc func showEditing(sender: UIBarButtonItem)
{
if(self.tableView.isEditing == false)
{
self.tableView.isEditing = true
self.navigationItem.rightBarButtonItem?.title = "Save"
self.tableView.reloadData()
}
else
{
self.tableView.isEditing = false
self.navigationItem.rightBarButtonItem?.title = "Edit"
self.tableView.reloadData()
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! SettingsCell
if self.tableView.isEditing == true {
cell.textField.isEnabled = true
if indexPath.section == 2 {
cell.textField.keyboardType = .phonePad
}
} else {
cell.textField.isEnabled = false
}
cell.selectionStyle = .none
filloutUserInfo(indexPath: indexPath, cell: cell)
return cell
}
override func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
return false
}
//THIS NEVER GETTING EXECUTED
override func tableView(_ tableView: UITableView, didEndEditingRowAt indexPath: IndexPath?) {
print("editing done for row \(indexPath?.item)")
}
override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return .none
}
}
Settings Cell:
class SettingsCell: UITableViewCell, UITextFieldDelegate {
let textField: UITextField = {
let tf = UITextField()
tf.isEnabled = false
return tf
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
override func layoutSubviews() {
super.layoutSubviews()
addSubview(textField)
textField.anchor(top: topAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 8, paddingBottom: 0, paddingRight: 8, width: 0, height: 0)
textField.addDoneButtonOnKeyboard()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The problem I've now encountered is that after I have gone into edit mode, and changed the text of a given cell, the tableview doesn't actually recognize this. The didEndEditingRowAt never gets called and that print statement is never displayed. I am suspecting that is has something to do with the textfield not being connected to the tableviewcontroller in any way, but I'm not sure how to fix this.
I need to be able to know when a user has finished editing in order to display an alert for improper formatting and disabling the save button.
You need to implement a callback to listen the textField endEditing event from SettingsCell to your ViewController.
To achieve this, here is the updated SettingsCell
class SettingsCell: UITableViewCell, UITextFieldDelegate {
let textField: UITextField = {
let tf = UITextField()
tf.isEnabled = false
tf.addDoneButtonOnKeyboard()
return tf
}()
public var onEndEditing: ((String?) -> Void)?
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
override func layoutSubviews() {
super.layoutSubviews()
textField.removeFromSuperview()
addSubview(textField)
textField.delegate = self
textField.anchor(top: topAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 8, paddingBottom: 0, paddingRight: 8, width: 0, height: 0)
}
func textFieldDidEndEditing(_ textField: UITextField) {
self.onEndEditing?(textField.text)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Now update cellForRowAt to listen the endEditing event as below,
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! SettingsCell
if self.tableView.isEditing == true {
cell.textField.isEnabled = true
cell.onEndEditing = { text in
print("Cell Editing finished with text: \(text)")
}
if indexPath.section == 2 {
cell.textField.keyboardType = .phonePad
}
} else {
cell.textField.isEnabled = false
}
cell.selectionStyle = .none
filloutUserInfo(indexPath: indexPath, cell: cell)
return cell
}