<Swift>How to navigate to a new page in custom cell? - swift

I have a custom cell FeedCell.swift and the controller FeedViewController.swift. And I set a btn in each cell so that I can navigate to detailController.swift. But in custom cell I can't use self.navigation. What should I do?

You can set an outlet of Button in cell and addTarget it like :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellID") as! yourCell
cell.yourButton.addTarget(self, action: #selector(goDetail), for: .touchUpInside)
return cell
}
#objc func goDetail(){
let vc = // Declare your viewController
self.navigationController?.pushViewController(vc, animated:true)
}
Btw you can add this action when a cell selected instead of using button
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let vc = // Declare your viewController
self.navigationController?.pushViewController(vc, animated:true)
}
If you want to send which cell's button clicked just add cell.yourButton.tag = indexPath.row in cellForRowAt and modify goDetail with
#objc func goDetail(sender : UIButton){
sender.tag // while give your selected cell index
}

Related

UITextField can't become first responder in UITableView

Context
Basic list. When user press '+', code creates a new item with default text that user can change.
Problem
I want to focus the new item as soon as user press '+' so that user can type desired name. I try to achieve this with this code:
func focus() {
title.becomeFirstResponder()
title.selectAll(nil)
}
But becomeFirstResponder() always returns false.
How can I give focus to UITextField in my UITableviewCell after creation ?
Here is full code of UITableViewController for reference:
import UIKit
class ViewController: UITableViewController{
var model: [String] = ["Existing item"]
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return model.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ItemUI", for: indexPath) as! ItemUI
cell.update(with: model[indexPath.item])
return cell
}
#IBAction func createItem(_ sender: UIBarButtonItem) {
let indexPath = IndexPath(item: model.count, section: 0)
model.append("new item")
tableView.reloadData()
tableView.scrollToRow(at: indexPath, at: .top, animated: true)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
let cell = self.tableView(self.tableView, cellForRowAt: indexPath) as! ItemUI
cell.focus()
}
}
}
class ItemUI: UITableViewCell{
#IBOutlet var title: UITextField!
func update(with: String) {
title.text = with
}
func focus() {
title.becomeFirstResponder()
title.selectAll(nil)
}
}
Ok I found the problem!
I was using method self.tableView(self.tableView, cellForRowAt: indexPath) instead of self.tableView.cellForRow(at: indexPath).
Here is what the documentation has to say:
"Never call this method yourself. If you want to retrieve cells from your table, call the table view's cellForRow(at:) method instead."
You can add a boolean in your view controller to keep track of adding item i.e isAddingItem with default value false and when you add new item simply update isAddingItem to true. In tableview cellForRowAt method check for last cell of tableview and if isAddingItem is true then selected all text of textfield and make it first responder.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ItemUI", for: indexPath) as! ItemUI
cell.update(with: model[indexPath.item])
if isAddingItem && indexPath.item == model.count {
// make your textfield first here and select text here
}
return cell
}
Also check if you have set textfield delegate.

Missing return in a function expected to return 'UITableViewCell' ; please help me

extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.deselectRow(at: indexPath, animated: true)
let vc = storyboard?.instantiateViewController(identifier: "tasks") as! TaskViewController
vc.title = "New Tasks"
vc.task = tasks[indexPath.row]
navigationController?.pushViewController(vc, animated: true)
//error:Missing return in a function expected to return 'UITableViewCell'
}
}
I try diffrence of return type but none of them are correct
The process is wrong in this function you are supposed to create a cell instance and work with it to be able to return it
Code modified:
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
/// Create a file for the cell let's say call it `myCell` with a label for task "let's suppose called `lblTaskOutlet`
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! myCell
myCell.lblTaskOutlet = tasks[indexPath.row]
return cell
}
}
You can add this vc.title = "New Tasks" in your viewDidLoad() :
self.title = "New Tasks"
The below line is another function called alone with its own body:
tableView.deselectRow(at: indexPath, animated: true)
tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell method is a tableview delegate method which populates the cells for each row. It is expecting a return type of UITableViewCell.
You can create UITableViewCell subclass TaskCell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
/// Create a file for the cell let's say call it `TaskCell` with a object for task "let's suppose called `taskObj`
let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell", for: indexPath) as! TaskCell
cell.taskObj = tasks[indexPath.row]
return cell
}
For the click action on cell you have to override another UITableViewDataSource method.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(identifier: "tasks") as! TaskViewController
vc.title = "New Tasks"
vc.task = tasks[indexPath.row]
navigationController?.pushViewController(vc, animated: true)
}
And please make sure that you have set tableview delegate and datasource in view did load of your view controller.
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
}

How to add table view cell with a button inside of another table view cell?

I am trying to make a to-do list app and I am having trouble trying to add subtasks to my main to-do tasks. I have a button inside each table view cell, when this button is pressed I want it to add another table view cell that I can type and save a "subtask" on.
//set up tableView row amount
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return taskData.count
}
//set up what is in the tableView row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//This is for a main task
let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell", for: indexPath) as! CustomTableViewCell
cell.label.text = taskData[indexPath.row]
return cell
}
Use delegates to pass data from cells to your ViewController, and reload the tableView.
CustomTableViewCell:
protocol CustomTableViewCellDelegate: class {
func customTableViewCellButtonClicked(_ cell: CustomTableViewCell)
}
class CustomTableViewCell: UITableViewCell {
weak var delegate: CustomTableViewCellDelegate?
func buttonClicked() {
self.delegate?.customTableViewCellButtonClicked(self)
}
}
ViewController:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell", for: indexPath) as! CustomTableViewCell
cell.label.text = taskData[indexPath.row]
cell.delegate = self
return cell
}
func customTableViewCellButtonClicked(_ cell: CustomTableViewCell) {
// add the task you need from that cell to your tasks list.
taskData.append(....)
//reload your tableView
self.tableView.reloadData()
}

Swift tableview cell doesn't save the state with different identifier

I am very confused on the reuse of the cells.
I have a table, each cell is a cell with a switch on it. If I toggle a switch I set the background color of that cell to a different color. However every time I scroll these changes don't persist.
I am subclassing UITalbeViewCell to create my own custom cell. Each cell has a different identifier. However when I scroll through the table, whatever changes I made to the cell still doesn't save. I've read similar questions but none of them worked.. Some suggested subclass which I did, some suggested use different identifier which I also did...
Here is the code of my tableview.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let key = Array(dataSource[indexPath.section].keys)[indexPath.row]
let cell = CellWithSwitch.init(style: .subtitle, reuseIdentifier: key)
cell.awakeFromNib()
let val = Array(dataSource[indexPath.section].values)[indexPath.row]
cell.switchView?.addTarget(self, action: #selector(self.switchChanged(_:)), for: .valueChanged)
if let index = key.firstIndex(of: "."){
cell.textLabel?.text = String(key.suffix(from: key.index(index, offsetBy: 1)))
}else{
cell.textLabel?.text = key;
}
cell.switchView?.setOn(val, animated: true)
return cell
}
You can change array value in switchChange action
lets i take array for switch as below:
var arrSwitch = [false,false,false,false,false,false,false,false,false,false]
Below is my cellForRowAt Method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell") as! customCell
cell. switchView.setOn(self.arrSwitch[indexPath.row], animated: false)
cell. switchView.tag = indexPath.row
cell. switchView.addTarget(self, action: #selector(self.onSwitchTap(_:)), for: .valueChanged)
return cell
}
Here is my onSwitchTap Action
#IBAction func onSwitchTap(_ sender: UISwitch) {
self.arrSwitch[sender.tag] = !self.arrSwitch[sender.tag]
}
Now on scroll it will persist last changes you have done.

Adding target to button to accessory View

I have created a button which I have assigned to my accessoryView. I get to see the created button, but when I press on it, nothing happens. Can anyone see what I'm doing wrong?
let accessoryButton = UIButton(type: .roundedRect)
accessoryButton.setTitle("message", for: .normal)
accessoryButton.sizeToFit()
accessoryButton.addTarget(self, action: #selector(goToMessaging(sender:)), for: UIControlEvents.touchUpInside)
cell.accessoryView = accessoryButton as UIView
#objc func goToMessaging(sender: UIButton){
print("message him")
}
try this
#objc func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = ...
cell.accessoryType = UITableViewCellAccessoryType.detailButton
cell.tintColor = .red
}
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
print("accessory Button Tapped = \(indexPath.row)")
}
Could it be because you are casting the button to a UIView?
accessoryButton as UIView
You could try adding a tap gesture recognizer to the accessoryView if it has to be a UIView.