Swift tableViewCell stays selected - ios

When I go selected a cell in my VC and go to another VC, and then back again to the first VC, the cell is still selected. I found some answers here and on the Medium but they didn't work.
I tried this in didSelectRowAt but it makes no sense because I will never be able to select a row if I put this at the beginning of the method:
tableView.deselectRow(at: indexPath, animated: false)
I also found this solution but it didn't work, too:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let selectedRow: IndexPath? = tableView.indexPathForSelectedRow
if let selectedRow = selectedRow {
tableView.deselectRow(at: selectedRow, animated: true)
}
else {
print("no selected rows found")
}
}
The method above always prints no selected rows found

Related

Cell stays highlighted and interactively unhighlights as we swipe

After we tap on the table view cells to push and pop to the detail view, if we swipe back to the previous table view, you'll notice that the cell stays highlighted and interactively unhighlights as we swipe.
How can this be programmatically implemented in UIKit?
The following reference illustrates the behaviour:
WWDC20 Introduction to SwiftUI: https://developer.apple.com/videos/play/wwdc2020-10119/?time=630
First, if you haven't already, you need to mark your "selected" when you tap on it:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = tableView.cellForRow(at: indexPath) as? SubclassedCell else {
return
}
//`setSelected(:animated:) is built into `UITableViewCell`
cell.setSelected(true, animated: true)
...
Then in viewWillAppear(_:) you're going to coordinate the deselection animation with the edge swipe animation:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//1
guard let selectedIndexPath = tableView.indexPathForSelectedRow else {
return
}
//2
if let transitionCoordinator = self.transitionCoordinator {
transitionCoordinator.animate(alongsideTransition: { (context) in
self.tableView.deselectRow(at: selectedIndexPath, animated: true)
}, completion: nil)
//3
transitionCoordinator.notifyWhenInteractionChanges { (context) in
if context.isCancelled {
self.tableView.selectRow(at: selectedIndexPath, animated: true, scrollPosition: .none)
}
}
} else {
//4
tableView.deselectRow(at: selectedIndexPath, animated: animated)
}
}
That's a LOT of code, here are the highlights:
Only run this if there's a selected index path. There's no selection if you’re on this screen for the first time. (Btw, the table view keeps track of its own selected index path(s). You just need to mark cells selected or not selected).
Coordinate the row deselection animation with the current animation "context" (i.e. the edge swipe animation context).
You might change your mind mid-swipe! If this happens, you want to re-select the thing you were deselecting.
Back in the day, before transition coordinators, you only had to add this one line. This else case is there in case there's no transition coordinator (old version of iOS, going back in the stack without animation, etc).
Ok..before you give up on UIKit, know there's a shortcut.
Shortcut: Use UITableViewController instead of UIViewController
Instead of subclassing UIViewController and adding a table view, just subclass UITableViewController. You still have to mark your cell selected, but that's it.
This works because UITableViewController has a property called clearsSelectionOnViewWillAppear, which is set to true by default. It takes care of everything for you.

tableview item looping through all items before displaying selected item siwift

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.

View did appear not called when view controller (SideMenu)

I have been implementing SideMenu in my iOS app (mailbox). The SideMenu is used to choose the current folder. I created a SideMenu navigation controller and attached a UITableViewController as the root vc.
It works perfectly fine, up until the point where I click on one of the cells, which dismisses the side menu and should load the mail in the original view controller. However, the original view controller's viewDidAppear method is not called. When I tried to call the load function directly, Xcode said that my tableView was nil (it wasn't when it first loaded).
Here is my code for didSelectRowAtIndexPath in the SideMenu table:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
currentFolder = folders[indexPath.row].path
tableView.deselectRow(at: indexPath, animated: true)
self.dismiss(animated: true) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "mail") as! MailViewController
vc.loadMail()
}
}
Here is the loadMail function in the original vc:
func loadMail(showsSpinner: Bool = true) {
...
if let messages = fetchedMessages as? [MCOIMAPMessage] {
self.mailsInFolder = messages
SwiftSpinner.hide()
self.refresher.endRefreshing()
self.table.reloadData()
}
}
Also, here is viewDidAppear in the original vc:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("viewDidAppear")
let from = self.navigationController?.transitionCoordinator?.viewController(forKey: .from)
if from is MessageViewController {
print("from is message controller")//should not load
return
}
self.loadMail()
}
Here, viewDidAppear is not called, and the mail doesn't load. What can I do to fix this? Thanks!
I had been working with SideMenu for a while, and I suggest you to use as mainViewController a UINavigationController and then when select one cell of your tableView in the rightViewController
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
currentFolder = folders[indexPath.row].path
tableView.deselectRow(at: indexPath, animated: true)
if let vc = self.storyboard?.instantiateViewController(withIdentifier: "mail") as! MailViewController
{
mainNavController.setViewControllers([vc], animated: false)
}
homeSlide.slideMenuController()?.closeLeft()
}
I hope this helps you, best regards

Bar Button Item in TableViewCell is not registering in didSelectRowAtIndexPath

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRow = indexPath.row
articleUser = userids2[indexPath.row]
self.performSegueWithIdentifier("jump", sender: self)
}
#IBAction func Share(sender: AnyObject) {
shareArticleUrl = articleUrls[selectedRow]
shareText = "Check Out This \(shareArticleUrl)"
let activityVC:UIActivityViewController = UIActivityViewController(activityItems: [shareText], applicationActivities: nil)
self.presentViewController(activityVC, animated: true, completion: nil)
}
I currently have a toolbar in each cell of a tableview. After the update to Swift 2, when a bar button item is selected, instead of running the didSelectRowAtIndexPath function first it instead jumps directly to the share function. I need to use the indexpath.row in the didSelect function to select the proper item in the articleUrl list. The technique above worked fine until the update to Swift 2.
I have tried to incorporate let selectedIndex = self.tableView.indexPathForCell(sender as! UITableViewCell) in the share function but I haven't been able to make that work.
Why don't you set the tag on your tab bar as row number while adding it in your cellForRowAtIndexPath: and then in your Share function just retrieve it from the passed in sender.

how to programmatically deselect a static cell iOS

I have a static cell and when clicked it launches a modal view. Except when i return from the modal view the cell is still selected? Why is it doing this and how can I make it only make the cell selected until the modal completely covers the view.
Thanks in advance
For Swift 2 :
override func viewWillAppear(animated: Bool) {
if let indexPath = self.tableView.indexPathForSelectedRow {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
}
For Swift 3 and 4 :
override func viewWillAppear(animated: Bool) {
if let indexPath = self.tableView.indexPathForSelectedRow {
tableView.deselectRow(at: indexPath, animated: true)
}
}
If you use a UITableViewController instead of a UIViewController this will be done automatically. Otherwise, you need to do the deselecting yourself using
deselectRowAtIndexPath:animated: on the UITableView. The best place to do this is probably on viewDidAppear: of the presenting view controller. That way, the user still sees the deselecting animation allowing them to reorient themselves.
If you don't need to track the selected row for other purposes, you can use
indexPathForSelectedRow to determine which index path needs to be deselected (if any).
I know this is too late but may help someone who's using Swift -
This will give a nice effect when you return to masterViewController from detailViewController
override func viewWillAppear(animated: Bool)
{
if let indexPath = self.tableView.indexPathForSelectedRow
{
self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
}
Otherwise add this delegate method of TableView and call it from didSelectRowAtIndexPath
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath)
{
if let indexPath = tableView.indexPathForSelectedRow
{
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
}
You can use this in your UITableViewController;
- (void)viewWillAppear:(BOOL)animated
{
[yourTableView deselectRowAtIndexPath:[yourTableView indexPathForSelectedRow] animated:YES];
}
In your condition, yourTableView property should be self.tableView
swift 3
override func viewWillAppear(animated: Bool) {
tableView.deselectRow(at: tableView.indexPathForSelectedRow!, animated: true)
}

Resources