仿iOS待办提醒APP的UITableView文本输入实现技术求助
Hey there! Let's work through your three issues step by step to replicate that smooth native iOS task/reminder input experience.
1. Add a New Cell When the User Presses Enter
Since you're using UITextView (not UITextField as shown in your existing code), we'll use the UITextViewDelegate method to listen for the enter key. Replace your current textFieldShouldReturn with this implementation:
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { // Catch the enter key press guard text == "\n" else { return true } // Get the current cell and its index path guard let cell = textView.superview?.superview as? tvcAddMetaDetails, let indexPath = tvAddMetaDetails.indexPath(for: cell) else { return false } let dataCount = isIngredients ? listItems.count : Method.count // Only add a new row if we're in an editable data row (not the "+" row) if indexPath.row < dataCount { // Save the current text (strip the newline) let updatedText = (textView.text as NSString).replacingCharacters(in: range, with: text).trimmingCharacters(in: .newlines) if isIngredients { listItems[indexPath.row] = updatedText } else { Method[indexPath.row] = updatedText } // Add an empty entry to the data source for the new row isIngredients ? listItems.append("") : Method.append("") // Refresh the table tvAddMetaDetails.reloadData() // Scroll to the new row and focus its text view DispatchQueue.main.async { let newIndexPath = IndexPath(row: dataCount, section: 0) self.tvAddMetaDetails.scrollToRow(at: newIndexPath, at: .bottom, animated: true) if let newCell = self.tvAddMetaDetails.cellForRow(at: newIndexPath) as? tvcAddMetaDetails { newCell.textView.becomeFirstResponder() } } } // Prevent the newline from appearing in the text view return false }
Make sure your tvcAddMetaDetails cell has a textView property, and you've set the view controller as its delegate in cellForRowAt.
2. Remove Hardcoded Number of Rows
Instead of hardcoding a row count, base it on your data source plus one extra row for the "+" add button. Update your numberOfRowsInSection method:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // Actual data entries + 1 row for the add button let dataCount = isIngredients ? listItems.count : Method.count return dataCount + 1 }
This way, the table will always show exactly your existing entries plus the add row—no hardcoding required.
3. Add a "+" Button for the New Row
First, make sure your tvcAddMetaDetails cell has a UIButton (we'll call it addButton) added to its layout. Then modify your cellForRowAt method to differentiate between data rows and the add row:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "AddMetaDetails", for: indexPath) as! tvcAddMetaDetails let dataCount = isIngredients ? listItems.count : Method.count if indexPath.row < dataCount { // Configure editable text view row cell.textView.isHidden = false cell.addButton.isHidden = true cell.textView.text = isIngredients ? listItems[indexPath.row] : Method[indexPath.row] cell.textView.delegate = self // Set delegate for text input handling // Keep your existing numbering if needed: // cell.textLabel?.text = "\(indexPath.row + 1). \(cell.textView.text ?? "")" } else { // Configure add button row cell.textView.isHidden = true cell.addButton.isHidden = false cell.addButton.setTitle("+", for: .normal) cell.addButton.titleLabel?.font = UIFont.systemFont(ofSize: 24, weight: .medium) // Add tap action for the button cell.addButton.addTarget(self, action: #selector(addNewRowTapped), for: .touchUpInside) } return cell } // Action for the "+" button tap @objc private func addNewRowTapped() { // Add empty entry to data source isIngredients ? listItems.append("") : Method.append("") tvAddMetaDetails.reloadData() // Scroll to the new row and focus its text view let newRowIndex = (isIngredients ? listItems.count : Method.count) - 1 let newIndexPath = IndexPath(row: newRowIndex, section: 0) tvAddMetaDetails.scrollToRow(at: newIndexPath, at: .bottom, animated: true) DispatchQueue.main.async { if let newCell = self.tvAddMetaDetails.cellForRow(at: newIndexPath) as? tvcAddMetaDetails { newCell.textView.becomeFirstResponder() } } }
Note: Adjust your cell's layout to center the addButton and match the clean, native iOS style.
Quick Additional Notes
- Keep your existing
heightForRowAtsetup (automatic dimension) to maintain dynamic cell heights as the user types. - Ensure your
tvcAddMetaDetailscell'stextViewhasisScrollEnabled = false(you mentioned you already have this covered).
内容的提问来源于stack exchange,提问作者captain_haddock

