Reputation: 1
I use an empty UITableView
with custom cells and I add new items one by one without any problem. The tableView is scrollable, however when I add an item to the cell that is one index more from the last visible cell the app crashes.
When the app is loaded the numberOfRowsinSection
is 1 and with every new entry it grows by 1. If the device has 10 visible cells it crashes on 11. If the device has 6 visible cells it crashes on 7. The app unexpectedly finds nil while unwrapping an Optional value.
Using advices from the question titled UITableview Not scrolling?
I tried each of the following lines in viewDidLoad
and in my function:
self.myTableView.delegate = self
self.myTableView.autoresizingMask = UIView.AutoresizingMask.flexibleHeight;
self.myTableView.rowHeight = UITableView.automaticDimension
self.myTableView.bounces = true;
self.myTableView.reloadData()
without any positive result.
Here is the code:
var enterCounts: Int = 1
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return enterCounts
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TextInputCell") as! TextInputTableViewCell
return cell
}
@IBAction func enter(_ sender: Any) {
let activeRow = self.enterCounts - 1
let index = IndexPath(row: activeRow, section: 0)
let cell: TextInputTableViewCell = self.myTableView.cellForRow(at: index) as! TextInputTableViewCell
if cell.myTextField.text == "" {
"DO NOTHING"
} else {
"DO STUFF"
enterCounts += 1
self.myTableView.reloadData()
let nextIndex = IndexPath(row: activeRow + 1, section: 0)
"This is the line that finds nil and crashes when row is out of view"
let nextCell: TextInputTableViewCell = self.myTableView.cellForRow(at: nextIndex) as! TextInputTableViewCell
nextCell.myTextField.text = ""
nextCell.myTextField.becomeFirstResponder()
}
}
I would expect the UITableView
to scroll and keep on loading as many cells the user enters, exactly as it does with the first/visible cells.Thank you.
After the 2 answers the code is:
@IBAction func enter(_ sender: Any) {
let activeRow = self.enterCounts - 1
let index = IndexPath(row: activeRow, section: 0)
let cell: TextInputTableViewCell = self.myTableView.cellForRow(at: index) as! TextInputTableViewCell
if cell.myTextField.text == "" {
"DO NOTHING"
} else {
"DO STUFF"
enterCounts += 1
let nextIndex = IndexPath(row: activeRow + 1, section: 0)
self.myTableView.insertRows(at: [nextIndex], with: .automatic)
self.myTableView.scrollToRow(at: nextIndex,at: .middle, animated: true)
//These lines are executed only when I am in visible cells
//when a new cell is added it is not ready to become first responder it is skipped and entered text is getting mixed up.
if let nextCell: TextInputTableViewCell = self.myTableView.cellForRow(at: nextIndex) as? TextInputTableViewCell {
nextCell.myTextField.text = ""
nextCell.myTextField.becomeFirstResponder()
}
}
}
With the code above the new cells appear wonderfully but textField become first responder only once, for the first cell that appears in view.
I declare my custom cell class in as below
@IBOutlet weak var myTextField: UITextField!
public func configure(text: String?, placeholder: String) {
myTextField.text = text
// myTextField.placeholder = placeholder
myTextField.accessibilityValue = text
// myTextField.accessibilityLabel = placeholder
}
override public func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override public func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
If I use a textField outside the tableView and keep the tableView only for displaying my entered values things are simple but having for entryField the last cell of the tableView creates problems when I try to make first responder the textField of the new inserted cell.
Upvotes: 0
Views: 395
Reputation: 214
if you want to add new line in you tableview whenever a enter button is tapped this I think you should try doing it this way
I am assuming that u just want a textfield in your tableview but this could work with other thing also in you custom class make an outlet for your textfield name it whatever you want I am naimg it tf and do this
iboutlet weak var tf: uitextfield!{
didSet{
tf.delegate = self
}}
and create a closure var like this
var textsaved: ((String) -> Void)?
then add textfield delegate to your customcell class like this
extension CustomCell: uitextfielddelegate{ }
then in your extension write :
func textfieldshouldreturn(_ textfield: uitextfield) -> Bool{
tf.resignFirstResponder()
retrun true }
func textfielddidendediting(_ textfield: uitextfield){
textsaved?(textfield.text!) }
then in your view controller create an empty array of string
var myarr = [String]()
make outlet for enter button and tableview
@iboutlet weak var mytableview: uitableView!{
didSet{
mytableview.delegate = self
mytableview.datasource = self }
@iboutlet weak var enterBtn(_ sender: uibutton) {
myarr.append("")
mytableview.reloaddata() }
in number of rows return myarr.count
in cell for row at
let cell = tableview.dequereuseablecell(withidentifier: "cell", for :indexpath) as! customcell
cell.tf.text = myarr[indexpath.row]
cell.textsaved = { [unowned self] (text) in
self.myarr.remove(at: indexpath.row)
self.myarr.insert(text, at: indexpath.row)
sel.mytableview.reloaddata()
} return cell }
Upvotes: 0
Reputation: 1396
It is crashing because apple only keeps cells in memory that are visible, In your case you are access cell that is not in memory and instead to use optional you are forcing to unwrap which causes the crash. After knowing this you should handle exception for cells that are not visible, like bewlow
@IBAction func enter(_ sender: Any) {
let activeRow = self.enterCounts - 1
let index = IndexPath(row: activeRow, section: 0)
let cell: TextInputTableViewCell = self.myTableView.cellForRow(at: index) as! TextInputTableViewCell
if cell.myTextField.text == "" {
"DO NOTHING"
} else {
"DO STUFF"
enterCounts += 1
self.myTableView.reloadData()
let nextIndex = IndexPath(row: activeRow + 1, section: 0)
//"This is the line that finds nil and crashes when row is out of view"
if let nextCell: TextInputTableViewCell = self.myTableView.cellForRow(at: nextIndex) as? TextInputTableViewCell
{
nextCell.myTextField.text = ""
nextCell.myTextField.becomeFirstResponder()
}
}
Or
if you want to make your textfield first responder first get cell in memory by scrolling to index Or by inserting it and then access it.
Upvotes: 1
Reputation: 476
if you need to add new cell you can use this line to add it:
let indexPath = IndexPath(row: ... , section: ...)
tableView.insertRows(at: [indexPath], with: .automatic)
after that scroll to it
tableView.scrollToRow(at: indexPath,at: .middle, animated: true)
finally, you can use this cell
let cell = tableView.cellForRow(at: nextIndex) as! YourCustomCellClass
Upvotes: 2