I have an application where I receive information via Alamofire and it gets passed into a tableview. I can further get more details about selected products which is passed to a view controller. this works fine but the issue I have now is when I click a cell to get more details about a particular item it always has to navigate through all other items. If I click an item on cell 5, the detail viewcontroller would display from item 1,2,3,4 then 5 although it does it quickly. I believe once I close the modal like
#IBAction func closeModalPressed(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
it is suppose to remove the previous details. how do I do it to make it show item 5 without looping through 1-4 first
did select row
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selected = Services.instance.items[indexPath.row]
Services.instance.selected = selected
let index = IndexPath(row: indexPath.row, section: 0)
tableView.reloadRows(at: [index], with: .none)
tableView.selectRow(at: index, animated: false, scrollPosition: .none)
NotificationCenter.default.post(name: NOTIFY, object: nil)
performSegue(withIdentifier: TO_DETAILS, sender: nil)
}
I don't think you are doing any api calls or anything in didSelectRowAt, I would suggest try following,
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selected = Services.instance.items[indexPath.row]
Services.instance.selected = selected
NotificationCenter.default.post(name: NOTIFY, object: nil)
performSegue(withIdentifier: TO_DETAILS, sender: nil)
}
You are unnecessarily reloading row here and asking table view to select same row again which ultimately calls didSelectRowAt.
Related
I have created a table view and add a custom table view cell . Inside the custom table view cell, i have added 4 labels and passed an array on each on of them . i have also enabled multiple row selection . i want to pass the selected rows to the next screen and display it there .
When you select row, every time UITableView didSelect call so you can make array for store value of selectedRow, If already in array than remove row.
selectedRow array you can pass in next screen use it.
For delete button i use mk_addTapHandler Button Extention you can see here
In my project same issue, but as per my design i put one button on cell for delete, i solved so You can use below code :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellDemo", for: indexPath)
cell.textLabel?.text = "\(indexPath.row)"
cell.btnDelete.mk_addTapHandler { (btn)
for (i,objValue) in selectedRow.enumerated() {
if indexPath.row == objValue {
self.selectedRow.remove(at: i)
for (i,objValue) in selectedRow.enumerated() {
if indexPath.row < objValue {
selectedRow[i] = objValue - 1
}else {
selectedRow[i] = objValue + 1
}
}
}
}
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedRow.append(indexPath.row)
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
for (index, element) in selectedRow.enumerated() {
if indexPath.row == element {
selectedRow.remove(at: index)
}
}
}
#IBAction func doneClick(_ sender: Any) {
if let VC = self.storyboard?.instantiateViewController(withIdentifier: "nextScreen") as! nextScreen
VC.tableViewSelectedRow = selectedRow
self.navigationController?.pushViewController(VC, animated: true)
}
}
If you have already enabled multi-row selection in the tableView it's relatively straightforward. In your button action method get the indexPaths of your selected rows and then use them to extract the data from your datasource and inject them into the next viewController. The below gives you core functionality but as you have not shared your code for datasource or table design, or indicated whether you are using IB or code for your app, you will have to make it fit your environment.
Assumptions:
you have a simple tableView with only one section and all rows are possible data for the next VC
you are doing your UI in code, and want to push rather than present the next VC
your data for your view is in an array called mainData
your next view controller is a class called NextViewController
#objc doneButtonTapped(_ sender: UIButton) {
let indexPaths = tableView.indexPathsForSelectedRows
let selectedData = indexPaths.map{mainData[indexPath.row]}
let nextVC = NextViewController()
nextVC.data = selectedData
navigationController?.pushViewController(nextVC, animated: true)
}
Actually I am trying to select and deselect multiple rows in tableview using image in tableviewcell,and also I want to delete selected rows when I click on delete button which is outside of the tableview.Here I am able to delete the selected row and am able to select and deselect single row.But I want to select and deselect multiple rows to delete when the rows are selected.Can anyone help me to do this.Thanks in advance.
//In tableviewcell class
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if selected{
checkOrUncheckImg.image = UIImage(named:"check")
}else{
checkOrUncheckImg.image = UIImage(named:"uncheck")
}
// Configure the view for the selected state
}
Create a dictionary or a set of the IDs or indexPath of the cells that are selected. I'm going to use IDs as they are more unique, but it really depends on your DB. If your objects don't have a unique identifier use indexPath
var arrayIDs = Set<String>()
Implement didSelectRowAtIndexPath tableView delegate method. When the user taps the cell, add or remove the ID to the arrayIDs
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let objectID = objects[indexPath.row].id
if (arrayIDs.contains(objectID)){
arrayIDs.remove(objectID)
}else{
arrayIDs.insert(objectID)
}
}
In your cellForRowAtIndexPath, if the arrayIDs contains the objects id, set selected image
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "yourCellClass") as? YourCellClass {
if (arrayIDs.contains(objectID){
cell.checkOrUncheckImg.image = UIImage(named:"check")
}else{
cell.checkOrUncheckImg.image = UIImage(named:"uncheck")
}
return cell
}
And when clicking the button outside of the cell
#IBAction func buttonPressed(_ sender: Any) {
//Do something with arrayIDs (loop through them and delete each one with REST call and from datasource or whatever you're doing, then reloadData to update table
tableView.reloadData()
}
I didn't test any of this, so there may be some small syntax errors, but you get the gist.
I'm replicating the sample Food Tracker app from apple and have an issue where the items from my TableView in Edit mode don't show the left hand delete icon (the round red one). If I swipe left I get a delete button on the right. I've downloaded the Food Tracker code and it works.
The difference I can see is that the sample app has implemented a UITableViewController, whereas I have a UITableView within a standard UIViewController. I'm not sure why this doesn't work.
There are some similar questions but they are older versions of Swift / Ios, so I'm not sure they are still relevant.
Here is the link to the sample app https://developer.apple.com/library/content/referencelibrary/GettingStarted/DevelopiOSAppsSwift/index.html#//apple_ref/doc/uid/TP40015214-CH2-SW1
Here are the funcs I recently added that should make it work
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
//delete the row from the dataSource
dataSource.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
}
else if editingStyle == .insert {
//create a new instance and insert the row
}
}
And I added this to the ViewDidLoad() to get the Edit button in the Nav bar
navigationItem.leftBarButtonItem = editButtonItem
Here's what the Food Tracker example looks like;
enter image description here
and mine is missing the left hand button. The only difference I can see is that I'm using a TableView inside a ViewController.
Thanks
Nick
No need to check the title of the barButtonItem as tapping it already toggles its state and passes the correct state in editing param.
Also, when overriding, you need to invoke super's implementation as absence would always show the delete indicator.
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: true)
tableView.setEditing(editing, animated: true)
}
I think I solved this. Effectively there's a fair bit of 'magic' that a UITableViewController is doing. To recreate this in a normal UIViewController with a TableView you have to override the SetEditing function and based on the title of the button put it in edit mode.
override func setEditing(_ editing: Bool, animated: Bool) {
let status = navigationItem.leftBarButtonItem?.title
if status == "Edit" {
tableView.isEditing = true
navigationItem.leftBarButtonItem?.title = "Done"
}
else {
tableView.isEditing = false
navigationItem.leftBarButtonItem?.title = "Edit"
}
}
here is my situation,
on my app, I get a list of post being display by a tablew view. on click, it bring to a postdetail view via this segue:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedIndexPath = indexPath.row
self.performSegue(withIdentifier: "push", sender: self)
self.feedTableView.reloadData()
}
With Firebase, some of my post has a condition published with boolean value ( true / false )
I 'd like somehow, if the value is on false, to disable the segue. As some posts have a 'COMING SOON' title, nothing should happen on click of those posts.
Do you know how is that possible to do by any chance ? Will be lovely !
Thanks a lot !
EDIT --
That the structure of my DB where you can see the published condition
Try this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard posts[indexPath.row].title != "COMING SOON" else {
return
}
self.performSegue(withIdentifier: "push", sender: self)
}
I don't think you can "disable" a segue, but you can prevent it to be performed using a conditional if statement in your didSelectRowAtIndexPath delegate method.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedIndexPath = indexPath.row
// get post info
let post = myArrayOfPosts[selectedIndexPath]
// check if post is COMING SOON
if post.title != "COMING SOON"{
// if not, trigger the segue
self.performSegue(withIdentifier: "push", sender: self)
}
self.feedTableView.reloadData()
}
I would recommend to store the "COMING SOON" string value in a constant in your class, or maybe in an Enum if you have other titles for which you want a different behavior.
EDIT: I've fixed the if statement which I incorrectly wrote in the first place. I cannot comment on posts yet (lack of reputation), so hopefully you see this edit :)
Is it possible to implement an undo manager when you delete a row in a tableView?
I have read about undo manager and you have to set the viewController as becomeFirstResponder().
The problem is that I am using a tableView inside a collectionViewCell
How shall I proceed with registering an undo?
I tried the following and it doesn't seem to work
#IBAction func tableButtonUndo(_ sender: Any) {
print("undo working")
undoItem()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
indexPathSelected = indexPath.row
if tableView == ordersTableView {
print("tap in kitchen table view")
self.removeItemKitchen()
ordersTableView.reloadData()
}
}
func removeItemKitchen() {
orderSingle.orderListFormatted.remove(at: indexPathSelected)
undoManager?.registerUndo(withTarget: MyVC(), selector: #selector(self.undoItem), object: nil)
undoManager?.setActionName("Undo")
}
func undoItem() {
undoManager?.registerUndo(withTarget: MyVC(), selector: #selector(self.removeItemKitchen), object: nil)
undoManager?.setActionName("Undo")
undoManager?.undo()
}
This code should undo the operation, but instead, I get a redo, it deletes the next row.
I would like to recover the deleted row by tapping the undo button