TableView not working after leaving and coming back to the view - ios

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.

Related

How do you launch a model UITableViewController from a UITableViewCell with a delegate?

So, I am pretty sure I am very close to getting this to work. I think it is maybe only the .delegate = line that doesn't work.
Firstly the protocol
protocol beever1: class {
func beever2()
}
Now in the UITableViewCell1:
var delegate1: beever1?
On long press, the below code runs in a funciton (I can't return via UIContextMenuConfiguration because there is a database check in UIContextMenuConfiguration - that's why I am presenting instead of returning).
if let delegate3 = self.delegate1{
delegate3.beever2()
}
Then in the UITableViewController1 that needs to be launched on the longpress
extension UITableViewController1: beever1 {
func beever2() {
let image3 = UIImage(named:"green.png")
if let unwrappedImage1 = image3 {
self.present(ImagePreviewController(image:unwrappedImage1), animated: true, completion: nil)
}
}
}
// I know this part works because I could successfull ypresent it on another VC
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? UITableViewCell1 {
**cell.delegate1 = self**.
// probably where error is but I think it makes sense as is: I am sending delegate info from cell to this UITableViewContoller bacause I want the UITableViewContoller to launch, right?///
return cell
}
return UITableViewCell()
}
Update:
I now think I understand why it isn't presenting, but am not sure how to fix it. UITableViewController1 is not loaded on screen when the long press is made. UITableViewCell1 on a 3rd TableViewController is loaded on screen. Then on long press UITableViewController1 needs to appear. So because UITableViewController1 is not loaded and only needs to be loaded on long press, the cellForRowAt function isn't automatically read
What I think I could do, but am not sure how to code. Option1) on the UITableViewCell1 I need to somehow code the delegate to be self.delegate1 = UITableViewController1, but currently that doesn't work
The Option 2 is the 3rd TableViewController on which the UITableViewCell1 is located. In it's cellForRowAt I could say: cell.delegate1 = UITableViewController1. But that also currently doesn't work
Do y'all agree with my assessment?

UITableView.reloadData() is not refreshing tableView. No error message

I've already looked at the post UITableView.reloadData() is not working. I'm not sure that it applies to my situation, but let me know if I'm wrong.
My app has a tableView. From the main viewController I am opening another viewController, creating a new object, and then passing that object back to the original viewController, where it is added to an array called timers. All of that is working fine. However, when I call tableView.reloadData() in didUnwindFromNewTimerVC() to display the updated contents of the timers array, nothing happens.
NOTE: I have verified that the timers array is updated with the new object. Its count increments, and I can access its members. Everything else in didUnwindFromNewTimerVC() executes normally. The tableView just isn't updating to reflect it.
Here is my code:
import UIKit
class TimerListScreen: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tabelView: UITableView!
var timers = [Timer]()
let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
tabelView.delegate = self
tabelView.dataSource = self
let tempTimer = Timer(timerLabel: "temp timer")
timers.append(tempTimer)
}
#IBAction func didUnwindFromNewTimerVC(_sender:UIStoryboardSegue){
guard let newTimerVC = _sender.source as? newTimerVC else{return}
newTimerVC.timer.setTimerLabel(timerLabel: newTimerVC.timerLabel.text!)
timers.append(newTimerVC.timer)
tableView.reloadData()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tabelView.dequeueReusableCell(withIdentifier: "TimerCell", for: indexPath) as? TimerCell{
let timer = timers[indexPath.row]
cell.updateUI(Timer: timer)
return cell
}else{
return UITableViewCell()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return timers.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 78
}
}
Thank you
Please note the spelling. There are two table view instances: the outlet tabelView and a (pointless) instance tableView.
Reload the data of the outlet
tabelView.reloadData()
and delete the declaration line of the second instance let tableView ....
However I'd recommend to rename the outlet to correctly spelled tableView (you might need to reconnect the outlet in Interface Builder).
And force unwrap the cell
let cell = tabelView.dequeueReusableCell(withIdentifier: "TimerCell", for: indexPath) as! TimerCell
and remove the if - else part. The code must not crash if everything is hooked up correctly in IB.

tableView reloadData off of delegate

Have a right view controller that slides in and out over the main view controller. This right view controller has a table in it to contain the passed information from the main.
I can access and pass the data to the controller from the main without issue but in the right view I need to then bind the data passed to it from the main.
The problem is that even though I try binding the data after the view comes into focus it gives nil on the tableView.reloadData().
RightViewController has 2 functions that are used by the main
func loadAlerts(){
self.tableView.reloadData()
}
func setAlerts(alerts: Alerts){
self.alerts = alerts
}
Alerts is just a custom object. It does contain values. self.alerts is a class variable.
MainViewController calls these 2 functions this way
self.rightViewController = self.storyboard?.instantiateViewController(withIdentifier: "RightViewController") as! RightViewController
Set the data after getting it from the api call
if let count = self.alerts?.Alerts.count {
if count == 0 {
return
}
//set on controller
rightViewController.setAlerts(alerts: self.alerts!)
}
This is defined at class level like
private var rightViewController: RightViewController!
Then I have a delegate defined for when the right controller is opened from a gesture and it calls like this
func rightDidOpen() {
rightViewController.loadAlerts()
}
This works fine for everything but the a tableView. Even by telling the tableView to load on the main thread like so
DispatchQueue.main.async{
self.tableView.reloadData()
}
Didn't change anything. At this point the alerts has values.
I don't mind refactoring the entire thing if need be so any ideas, thoughts or info of how I can get this to work is appreciated. If more info is needed just let me know.
--
Here the table delegate and source defined
class RightViewController : UIViewController, UITableViewDataSource, UITableViewDelegate
and from front end assigned to the uicontroller (its calle Alerts Scene). Forgot to mention that if I do the api call directly in the right controller it works fine but I'm trying to reduce api calls so am refactoring this.
Here are the methods. Sorry for the misunderstanding.
//MARK: Tableview delegates
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let count = alerts?.Alerts.count{
return count
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let alertD = alerts?.Alerts[indexPath.row] {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "AlertTableViewCell") as! AlertTableViewCell
cell.name.text = alertD.Summary
cell.icon.image = Helpers.listImage24dp(id: alertD.TOA)
cell.selectionStyle = .none
cell.name.textColor = UIColor.blue
return cell
}
return UITableViewCell()
}

Swift View Controller with UITableView sections

I've been searching for awhile without luck. I am trying to find an example of a View Controller with a UITableView that has sections. The examples I've see are all dealing with a Table View Controller which I cannot use as I have need of buttons in the same view which control the content of the table view. Anyone have an example, know of an example or have an idea about to implement such? Thanks.
Edit
I've got a table view in a view controller, get the data from an api call, separate the sections and data in an array of a struct. I then send this to be bound to the table view. Doing so throws
[UIView tableView:numberOfRowsInSection:]: unrecognized selector sent to instance
but I don't understand where the problem is.
Code for the tablview
//MARK: Tableview delegates
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if let count = incidentDataSection?.count{
return count
}
return 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (incidentDataSection?.count)! > 0{
return incidentDataSection![section].incidents.count
}
return 0
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return incidentDataSection?[section].title
}
/*
func tableView(tableView: UITableView, iconForHeaderInSection section: Int) -> UIImage? {
return incidentDataSection?[section].icon
}*/
//if clicked, will openn details view passing in the details
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//let incidentDetails = incidentData?[indexPath.row]
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let section = incidentDataSection?[indexPath.section] {
let cell = tableView.dequeueReusableCell(withIdentifier: "IncidentTableViewCell") as! IncidentTableViewCell
cell.roadNameLabel.text = section.incidents[indexPath.row].RoadWay
cell.whenLabel.text = section.incidents[indexPath.row].DateCreated
cell.statusLabel.text = section.incidents[indexPath.row].DateCleared
return cell
}
return UITableViewCell()
}
incidentDataSection is an array of a struct which has the section title and the different items.
Answer
Though I received some fairly good feedback, the cause was actually a typo. Looking closely at
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return incidentDataSection?[section].title
}
you'll notice the problem is that there is no underscore before tableView:. What was happening is that the datasource and delegate were skipping over the functions since with and without call different protocols in swift 3. Thanks to thislink I was able to figure out the cause. My bad for forgetting to mention this was in Swift 3. Might had saved everyone some time.
You need a tableview instance in your view controller.
Implement the protocols UITableViewDelegate, UITableViewDataSource in your view controller as a UITableViewController.
Don't forget bind the tableview in XIB with tableview in the class.
Look this sample:
class Sample01ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView: UITableView?
override func viewDidLoad() {
super.viewDidLoad()
tableView?.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
self.tableView?.reloadData()
}
// ...
You have the required methods implemented, however it sounds like you need to "subclass" or "subcribe" to the UITableView's delegate and dataSource. By using:
class MyViewController : UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView : UITableView!
}
Now that you have those protocols you will need to set your tableView's delegate and dataSource to your viewController. You can do this using storyboard by drag and drop, or inside of your viewDidLoad() which is what I always do because it is easy for other developers to see from the start of opening your code where your delegate and dataSources are assigned to. Using:
#override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
Then your delegate methods and dataSource methods in your viewcontroller will be called for that tableView. Then you can add the IBOutlets to UIButton/UILabel/UISwitch, etc... and do what you will with your ViewController without being limited to simply using a table view inside of that view controller. I Almost always use this methods when using UITableViews/UICollectionViews even if I set the tableView/collectionView to be the size of the whole view because I like the freedom of using a UIViewController over a UITableViewController/UICollectionViewController.
*Note numberOfRows() is not required but I always override it as well, just kind of a habit at this point. Also you sound new to iOS development, so if you aren't already, the next thing I would look into after getting your tableView up and running is pulling your data from your API on a background thread to keep your mainThread open for user response on your UI, DispatchQueue. This is really important if you are displaying images from the API.

UITableViewCells not appearing in second Tab

I have the following problem:
I am making a Pokédex-like application that displays a list of all 721 Pokémon on the first tab, and another list on the second tab containing My Favorite Pokémon. Essentially, there are two identical ViewControllers connected to my TabBar.
My storyboard is as follows:
So here is the problem:
The TableView on the first (and initial) tab works fine. However, when I load the TableView on the second tab the Pokémon are loaded, but not displayed. I am able to click the TableViewCell and go to the detail page, but the label in the TableViewCell is not showing anything.
This is the code I use for loading Favorites TableView
class FavoritesViewController: BaseViewController,
UITableViewDataSource, UITableViewDelegate {
#IBOutlet var FavoritesListView: UITableView!
var pokemonList: [String] = ["Nothing Here!"]
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("FavoriteCell", forIndexPath: indexPath) as! FavoriteCell
var name = pokemonList[indexPath.row]
capitalizeFirstLetter(&name)
cell.nameLabel.text = name
return cell;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pokemonList.count
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print(pokemonList[indexPath.row])
self.performSegueWithIdentifier("ToPokemonDetail", sender: pokemonList[indexPath.row])
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "ToPokemonDetail"){
let destination = segue.destinationViewController as! PokemonDetailViewController
let thisPokemon = sender as! String
destination.currentPokemon = thisPokemon
}
}
override func viewWillAppear(animated: Bool) {
FavoritesListView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
// Fetch the cached list, getNames returns an array of strings
let list = utility.getNames("Favorites")
pokemonList = list
}
The delegate and the dataSource are set via the storyboard.
The above code works, and shows the Favorites list just fine. The class for the complete Pokédex has a similar construction.
I have tried switching Favorites and Pokédex around, so that it shows the complete Pokémon list on startup. All 721 Pokémon are shown correctly, but then the Favorites are not visible.
What else I have tried:
Checking the Reuse Identifiers, over and over
Referencing outlets should be bound correctly
Calling TableView.reloadData() in the viewDidAppear method
Switching around the tab items
Does anyone have any clue what on earth is going on here?
Feel free to ask any more questions
Edit: this is what happens when I swap the two TabBar Buttons around, no code changes
Pokédex Screen
Favorites Screen
GitHub Project Here
Problem is in storyboard cell label frame. Set constraints of view controller for (Any,Any) Size Class. I can commit the code on github if you can give me write rights on your git. Thanks
Perhaps your table's delegate and dataSource are not set.
table.delegate = self
table.dataSource = self
Of course this is after you add the properties to your view controller
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
Your number of rows is always 0 for that controller,
I looked into your code pokemonList count is always 0 its not updating data in it
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pokemonList.count
}
The big issue is your PokemonDetailViewController is not a UITableViewController. It needs to inherent from UITableViewDataSource, UITableViewDelegate and then be connected to the storyboard view to provide data and formatting for a table.

Resources