didEndEditingRowAt not called with custom tableview cell - ios

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
}

Related

Call contextMenuConfigurationForRowAt on Click instead of Long Press

I took my lead from this tutorial: https://kylebashour.com/posts/context-menu-guide
I thought I had everything, yet when I click on the row, nothing happens
UPDATE: Thanks a comment I learned that contextMenuConfigurationForRowAt is called on a long press. Is there a way to call this on a click? I wanted to give users several options for contacting the people listed. But buttons in UITableViewCell doesn't seem to work, so a context menu is the best I can think of, but a long press isn't obvious for interacting with this table view.
import UIKit
class PeerSupportVC: UIViewController {
#IBOutlet weak var peerSupportTableView: UITableView!
var supportArray: NSArray = [];
fileprivate var configuration = Configuration.sharedInstance;
override func viewDidLoad() {
super.viewDidLoad()
self.peerSupportTableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
self.peerSupportTableView.delegate = self;
self.peerSupportTableView.dataSource = self;
self.peerSupportTableView.rowHeight = 200.0
getPeerSupport();
}
func getPeerSupport(){
self.supportArray = ["One","Two","Three"]
DispatchQueue.main.async(execute: {
self.peerSupportTableView.reloadData()
});
}
}
extension PeerSupportVC: UITableViewDelegate {
func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
let item = self.supportArray[indexPath.row]
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { suggestedActions in
let phoneAction = UIAction(title: "Phone Call", image: UIImage(systemName: "person.fill")) { (action) in
DispatchQueue.main.async(execute: {
print("Wants to Phone Call");
});
}
let textAction = UIAction(title: "Text Messsage", image: UIImage(systemName: "person.badge.plus")) { (action) in
DispatchQueue.main.async(execute: {
print("Wants to Text Message");
});
}
return UIMenu(title: "Contact Options", children: [phoneAction, textAction])
}
}
}
extension PeerSupportVC: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("Count of Support Array:",self.supportArray.count);
return self.supportArray.count;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath);
for viewToRemove in cell.subviews {
viewToRemove.removeFromSuperview();
}
let width = self.peerSupportTableView.frame.width;
if let result = self.supportArray[indexPath.row] as? String{
print(result);
let NameLabel = UILabel(frame: CGRect(x: 180, y: 0, width: width-155, height: 30));
NameLabel.textColor = .black;
NameLabel.text = " "+(result);
NameLabel.font = UIFont.systemFont(ofSize: 12, weight: UIFont.Weight(rawValue: 400));
NameLabel.adjustsFontSizeToFitWidth = true;
cell.addSubview(NameLabel);
}
return cell;
}
}
I dumbed the code down to share, but this lesser version still doesn't work.
What I want to happen is a menu to pop up on the line that was selected and give the user options on how to contact the person they selected. Any help on displaying the menu would be greatly appreciated.
For reference, here's your code, modified to use a reusable cell:
// simple cell class, based on your code
class PeerCell: UITableViewCell {
let nameLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
nameLabel.translatesAutoresizingMaskIntoConstraints = false
nameLabel.font = .systemFont(ofSize: 12, weight: UIFont.Weight(rawValue: 400))
contentView.addSubview(nameLabel)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
nameLabel.topAnchor.constraint(equalTo: g.topAnchor),
nameLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 180.0),
nameLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
nameLabel.heightAnchor.constraint(equalToConstant: 30.0),
])
}
}
class PeerSupportVC: UIViewController {
#IBOutlet var peerSupportTableView: UITableView!
var supportArray: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
// register our custom cell
self.peerSupportTableView.register(PeerCell.self, forCellReuseIdentifier: "Cell")
self.peerSupportTableView.delegate = self;
self.peerSupportTableView.dataSource = self;
self.peerSupportTableView.rowHeight = 200.0
getPeerSupport();
}
func getPeerSupport(){
self.supportArray = ["One","Two","Three"]
}
}
extension PeerSupportVC: UITableViewDelegate {
func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
let item = self.supportArray[indexPath.row]
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { suggestedActions in
let phoneAction = UIAction(title: "Phone Call", image: UIImage(systemName: "person.fill")) { (action) in
print("Wants to Phone Call");
}
let textAction = UIAction(title: "Text Messsage", image: UIImage(systemName: "person.badge.plus")) { (action) in
print("Wants to Text Message");
}
return UIMenu(title: "Contact Options", children: [phoneAction, textAction])
}
}
}
extension PeerSupportVC: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("Count of Support Array:",self.supportArray.count);
return self.supportArray.count;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! PeerCell
let result = self.supportArray[indexPath.row]
print(result)
cell.nameLabel.text = result
return cell;
}
}
As I said in my comment, though, your contextMenuConfigurationForRowAt was working as-is -- just need to long-press on the cell to trigger the call.

Using a programmatic custom UILabel in a UITableView

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 .....

Can't return a value from UITextField to UILabel in UITableViewCell as subviews

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.

Inserting table view row by pressing the button

I created a UITabBarController with two controllers , as first I have a
ViewController with UIButton on it and the second is UITableViewController, how can I by pressing on btn in ViewController insert new row in tableView?
Should it be done with custom delegation ?
Can someone please show quick example
here is my code
import UIKit
class Home: UIViewController {
var delegate: TableViewDelegate?
lazy var addButton : UIButton = {
let button = UIButton(type: .system)
button.setImage(#imageLiteral(resourceName: "plus_photo"), for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(setupRows), for: .touchUpInside)
return button
}()
#objc func setupRows() {
delegate?.insertingRows()
}
override func viewDidLoad() {
super.viewDidLoad()
delegate = self as? TableViewDelegate
setupRows()
navigationItem.title = "Test"
view.backgroundColor = .white
view.addSubview(addButton)
addButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
addButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
addButton.heightAnchor.constraint(equalToConstant: 150).isActive = true
addButton.widthAnchor.constraint(equalToConstant: 150).isActive = true
}
}
import UIKit
protocol TableViewDelegate {
func insertingRows()
}
class TableViewTest: UITableViewController , TableViewDelegate {
var items = ["abc", "abcd", "abcde"]
let cellid = "cellid"
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(CustomCell.self, forCellReuseIdentifier: cellid)
insertingRows()
}
func insertingRows() {
let indexPath = IndexPath(row: items.count + 1, section: 0)
tableView.insertRows(at: [indexPath], with: .left)
self.tableView.reloadData()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellid, for: indexPath) as! CustomCell
cell.textLabel?.text = items[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
}
class CustomCell: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
}
}
You can use tabBar(_:didSelect:) delegate method in your Home viewController.
Your Home VC :
class Home: UIViewController, UITabBarControllerDelegate {
private var selectedIndex: Int?
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarController?.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if tabBarController.selectedIndex == 1 {
if let secondViewController = viewController as? TableViewTest, let index = selectedIndex {
secondViewController.insertingRows(index: index)
}
}
}
#objc func setupRows() {
selectedIndex = 3
}
}
Your TableViewTest TableViewController :
class TableViewTest: UITableViewController {
func insertingRows(index: Int) {
let indexPath = IndexPath(row: index, section: 0)
tableView.beginUpdates()
tableView.insertRows(at: [indexPath], with: .none)
tableView.endUpdates()
}
}

Swift - Could not cast value of 'UITableViewCell'

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")

Resources