TableView DidSelectRowAt() latency - uitableview

Hello I actually use a tableview in my app. I have this lines of code which switch from a VC to a mother and it work fine everywhere :
let vue = MAINSTORYBOARD.instantiateViewController(withIdentifier: "accueil") as! Accueil
self.present(vue, animated: true, completion: nil)
In my tableView I use the function DidSelectRowAt ( this execute when I click on a row/cell) to do this (switch of VC)
But you've already understood when I click it work but with latency.
This latency can is sometimes 1-2s sometimes up to 12s it's random. That's weird.
I specify the function above work instantly with buttons and others.
this is the code (I write only important things) :
class Horaire: ViewController, UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vue = MAINSTORYBOARD.instantiateViewController(withIdentifier: "accueil") as! Accueil
self.present(vue, animated: true, completion: nil)
}
and the other class :
class Accueil: ViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("viewDidLoad executed")
}
}
So when I click on a cell of my tableview it immediately print "viewDidLoad executed" but VC switch is done only after some random seconds.
Why does I have this latency and how to fix it ? it don't use segue

The first thing that came to my mind was to put the block in the main queue. I know didSelectRowAt is supposed to run in the UI thread, but I saw on the internet a couple complaints about this very same thing. Looks like a bug to me.
class Horaire: ViewController, UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
DispatchQueue.main.async{
let vue = MAINSTORYBOARD.instantiateViewController(withIdentifier: "accueil") as! Accueil
self.present(vue, animated: true, completion: nil)
}
}
}
You can see for some odd reason this seemed to work for these people:
https://github.com/HeroTransitions/Hero/issues/79

Related

How can I pass data from "didSelectRowAt" to another VC without presenting it or a segue?

What I basically want is I want to press on a row in my TableViewController which then takes, for example, the text that the row has and passes it to a ViewController that is currently not present. Like when you click on a song in the Spotify app and it plays it without presenting anything but the song details are shown in the mini-player.
Does anyone have a clue how to do that?
Well you can do that but you need to initialize your View Controller first for you to access its properties like:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DestinationVc") as? DestinationVc
vc?.text = "TextToBePassed"
}
And there it is without presenting it but I don't get it why you don't want to present or show it but that's how it is done based on your question. Thanks :)
If you have created the UI via XIB or programatically then follow this approach:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let nextVC = NextViewController()
nextVC.text = model?[indexPath.row].text // If you are using model or if you have stored the data in the array then nextVC.text = yourArray[indexPath.row]["text"]
self.navigationController?.pushViewController(nextVC,animated: true)
}
Also, in the NextViewController, you have to add text. Like this:
class NextViewController: UIViewController {
var text = String()
//MARK:- View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
print(text) //This will print the text passed from previous VC
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = NextViewController()
vc.title = "\(Title)"
self.navigationController?.pushViewController(vc,animated: true)
}
Using this method can help you in passing the data to another Controller without using segue.
Assuming this is for iOS as the question doesn't specify.
In the tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) method in your UITableViewDelegate, you can grab the cell's text with
let cell = tableView.cellForRow(at: indexPath)?.textLabel?.text // cell: String?
You can then assign this to a global variable if you wish, or if you maintain a reference to the non-present view controller, you can give it a property to store this text and assign it before you call performSegue(withIdentifier identifier: String, sender: Any?).

TableView not working after leaving and coming back to the view

I am puzzled by the behavior of tableView if you leave their view and come back.
I have a screen with one tableView in it that works when I first enter the view. Adding, removing, and updating table cells work. However, when I press a button to segue into the next view and immediately come back, the tableView no longer works. The code that is supposed to execute ( tableView.reload() and all the associated methods) run as they should. However, the screen does not get updated even though internally the arrays get updated, and reload gets ran and executes the code that should update the screen( that is, tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell runs jus fine).
What am I missing? Does tableView require any special treatment if I leave the view and come back to ti?
Thanks.
Edit:
The code for the class where the tableView is something like:
class DebugViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak var table: UITableView!
var array = [M]()
override func viewDidLoad() {
super.viewDidLoad()
self.searchbar.delegate = self
self.table.delegate = self
self.table.dataSource = self
search_view = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Search_view") as? SomeViewController
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cellManage") as? TableCellManage else { return UITableViewCell() }
let idx = indexPath.row
let value = array[idx]
cell.lbl_time.text = value.month
cell.lbl_background.text = value.color
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 130
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.array.count
}
#IBAction func tapped_addsongs(_ sender: Any) {
self.present( search_view, animated: true, completion: nil)
}
}
Where is the tableView.reload() part? The issue might be generated because you're trying to update the table in a background thread. All UI Updates must be done in main thread:
func someFunc() {
...
DispatchQueue.main.async {
tableView.reload()
}
...
}
After looking at pretty pictures of the life cycle of apps and googling I found the issue and the solution.
The problem was that I had listeners set up to update my table view in a troublesome way. Specifically, I was using the viewDidAppear/viewDidDisappear to bring up and down the listeners, and there was some conflict in the code that managed the state because of this.
Instead, I now bring up the listeners on viewDidLoad. They stay active, regardless of how many views are pushed (within reason, but I only push one), and update my tableview so that when I come back to that view everything is already updated. I don't even see the updates happening, they happen before I get to my view. As for detaching the listeners there is a handy-dandy function I did not know about until 5 minutes ago: deinit. This is the equivalent of destructor in Swift, so I detach my listener when my class object for this view is released from memory.
That solves my issue...and increases performance and I no longer have dangling connections for not managing the listeners well. So a win-win-win.
Thank you all for trying to help! I hope this helps other folks.

I amt trying to dismiss my tableViewController in override func tableView(...didSelectRowAt...), but nothing happens

I want to return to my previous viewController, so I used the dismiss method, but when I select a cell nothing happens. This is the code that I have right now.
override func tableView(_ tableView: UITableView, didSelectRowAtindexPath: IndexPath) {
delegate?.dataReceived(data: universityArray[indexPath.row].name)
dismiss(animated: true, completion: nil)
}
Form the top of my head, i can see two potential problems here
The tableView delegate is not set
tableView.delegate = self
The viewController was not presented rather pushed, of that is the case try popViewController instead of dismiss
self.navigationController?.popViewController(animated: true)
Hope the problem was one of the above.

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

Use of unresolved identifier in UITableViewDelegate extension

Instead of assigning the view controller as the UITableViewDelegate, I'm trying to reduce the code in the view controller by creating an extension for the UITableViewDelegate.
Why am I getting the error "Use of unresolved identifier companyDetailVC" for the line companyDetailsVC = CompanyDetailsViewController() when that is correct Swift 3 syntax?
Code
extension TableViewDelegate: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
companyDetailsVC = CompanyDetailsViewController()
self.present(companyDetailsVC, animated: true, completion: nil)
}
}
Edit: I'm trying to do this programmatically without storyboard. I created a UITableViewDelegate extension because I'm trying to reduce the code in the View Controller.
The code for presenting the Viewcontroller should be somewhat like this
and the extension should be like
extension YourClassNameHere: UITableViewDelegate {
//then your did select method comes here and in that put this code for presenting the viewcxontroller
let companyDetailsVC = CompanyDetailsViewController() //change this to your class name
self.present(companyDetailsVC, animated: true, completion: nil)
}
Replace your code with below code. You will need to provide your ViewController to create its extension and to use the property and method of it.
Make sure companyDetailsVC is declared in the controller. You don't need to use self until its called from block.
extension yourViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.companyDetailsVC = CompanyDetailsViewController()
self.present(companyDetailsVC, animated: true, completion: nil)
}
}

Resources