Calling a function once after tableview.reloadData - ios

Every time I reload my table by calling
tableview.reloadData()
I want to call a specification function like
myFunction()
I was wondering, instead of stacking these two function next one after another everywhere in my code like
tableview.reloadData()
myFunction()
Is there a smart and clean way of calling myFunction every time tableview reloads?

There is no delegate method to give you a callback when reloadData() has completed, but to make it cleaner you could do a couple of different things.
You could create your own function like this:
func reloadTable() {
tableView.reloadData()
myFunction()
//plus anything else you want to accomplish
}
Then you call that function everywhere in one line instead of repeating your code.
Alternatively, you could subclass UITableView and override the reloadData() method, adding your additional functionality.

one way is to use inheritance. just implement your own tableview class and reload the reloadData function of UITableView.
class YourTableView: UITableView {
override func reloadData() {
super.reloadData()
myFunction()
}
func myFunction() {
//do something
}
}
then declare YourTableView instead of UITableView

Related

iOS Protocol function call on viewDidLoad/setup

I'm trying to create a protocol that I can use on UIViewControllers that will do some setup work when the protocol is attached to a UIViewControler. I currently have the following code.
protocol MyProtocol {
}
extension MyProtocol where Self: UIViewController {
func setup() {
print("We have successfully setup this view controller")
}
}
class MyViewController: UIViewController, MyProtocol {
override func viewDidLoad() {
super.viewDidLoad()
print("Other setup work here") // This line might or might not exist
setup() // Goal is to remove this line, so I don't forget to add it across all my view controllers
}
}
My question is, is there a way to remove the setup() call from within the viewDidLoad function? I think it'd be a lot safer to not have to call that function every time. If there is a view controller that forgets to add that one call, then the setup won't happen, and I want to try to prevent that.
There is a chance that the viewDidLoad function on the view controller that it is attached to will do other work (ex. in this example print("Other setup work here")), or there is a chance it won't do anything except for that setup call.
I'm also not completely opposed to moving that setup function call into a separate function within the view life cycle, but again those other functions in the view life cycle that get called might have other things that need to run as well, so I don't want to completely override them.
I have also considered using the init method somehow, but I think the problem with that is that the view won't have been loaded yet and therefor I can't do the proper setup work like changing a label's text and such.

Who is calling reloadData method for UITableView

It can sounds weird but I don't understand why my tableView is showing cells.
I got array of items that should be shown in cells but I don't run reloadData method of my tableView anywhere in my code. It seems that some of app components or maybe frameworks inside app is calling reloadData method and I want to find out which one?
How it can be done?
A table view loads itself the first time it is added to the window hierarchy. You don't need an explicit call to reloadData for the table to load itself initially.
If you want to see how this is really done, put a breakpoint on your table view data source methods and bring up your table view. Look at the stack trace in the debugger to see the sequence of events.
If your data preparation takes some time and you do not want the table view to show any data initially you could use an approach like this:
class TableViewController: UITableViewController {
var someDataSource: [Any]!
var dataSourcePrepared = false {
didSet {
tableView.reloadData()
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
guard dataSourcePrepared else { return 0 }
return someDataSource.count
}
func doSomePreparationStuff() {
// ...
// ...
someDataSource = ["Some", "Content"]
dataSourcePrepared = true
}
}
In this case I used a Bool variable dataSourcePrepared which is false initially. As soon as you have prepared your content set it to true and the table view gets reloaded.

Method tableView.reloadData() asynchronous execution

My code:
...
self.tableView.reloadData()
self.someOldValue = self.someNewValue
...
TableView delegate and datasource methods work with self.someOldValue. I need use self.someOldValue in those methods before it changes. How to do it? Method reloadData() works asynchronous and tableView delegate and datasource methods works with newValue already(self.someOldValue = self.someNewValue executes before self.tableView.reloadData())

Calling reloadData through delegate

I have a table view which conforms to custom protocol FoodItemProtocol and it implements its funciton:
func foodItemWasTaggedAsFavorite() {
tableView?.reloadData()
print("foodItemWasTaggedAsFavorite")
}
After foodItem is tagged as favorite, this function is called and print statement is executed, however table view is never reloaded.
I realized I don't actually need to use delegation for this, it works fine if I call to reloadData() in viewDidAppear(). But still I'd like to know why it's not working through delegation? I've even tried to call reloadData() on main thread like this:
dispatch_async(dispatch_get_main_queue()) {
tableView?.reloadData()
}
But I got same result.
If you are calling the delegate method from a different view controller, tableView will be nil.
To check this, modify foodItemWasTaggedAsFavorite to be:
if let tableView = tableView {
tableView.reloadData()
print("foodItemWasTaggedAsFavorite")
}
Now check if the print statement is being printed. I'm pretty sure it won't, because tableView is nil.
However, in viewDidAppear:, the table view has already been loaded, so it isn't nil.
Also, there is no reason to reload the data if the table view isn't on screen anyways.

ios swift parse: methods with async results

When I go to a viewController I call within my viewDidAppear Method a function:
override func viewDidAppear(animated: Bool) {
getLessons()
}
This methods loads from parse.com a list of data I want to use in a pickerView.
The function itself:
func getLessons(){
var query = PFQuery(className:"Lesson")
query.orderByAscending("name")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
for object in objects {
var name = object["name"] as String
self.languagePickerKeys.append(object.objectId)
self.languagePickerValues.append(name)
self.selectedLanguage.text = self.languagePickerValues.first // set the first lessons name into the text field
self.selectedLessonObjectId = self.languagePickerKeys.first // set the first objectId for the lesson
self.languagePicker?.reloadAllComponents()
}
} else {
// Log details of the failure
println("\(error.userInfo)")
}
}
println("getLessons done")
}
The thing is, that the textfield is empty, as the getLesson() gets the data async and the data is not available to the textfield.
I also tried to put the getLesson into the viewDidAppear method, but this doesn't help me, the textfield is empty anyway.
What can I do, to have the data from the getLessons() method ready and loaded its first value into my textfield when the view is shown to the user?
You certainly have to get the data from asyncTask before setting it to pickerView.
Here's the ViewController lifecycle after instantiation:
Preparation if being segued to.
Outlet setting
Appearing and Disappearing.
So, you have two options:
Load the data in previous ViewController and then perform the segue. You need to follow these steps for it.
a. Create a segue from previous ViewController to your ViewController.
b. Call the function when you want to go next ViewController which fetches the data, and the end (after getting the data) call performSegueWithIdentifier which will lead to your ViewController.
c. Set the data in prepareForSegue
let navigationController = segue.destinationViewController as UINavigationController
navigationController.data = yourData //you got from async call
Now when you reach your ViewController, you are sure that your data is present, and you can set it to your pickerView.
If you want to do it in the same ViewController: here's is the lifeCycle of ViewController:so you need to call your function in viewDidLoad, and always set your pickerView after completion of the async network call.
Make sure that you initiate all changes to the UI from the main thread e.g. like so:
dispatch_async(dispatch_get_main_queue(), {
selectedLanguage.text = languagePickerValues.first
self.languagePicker?.reloadAllComponents()
})
The problem is that findObjectsInBackgroundWithBlock is an asynchronous method, so even if you fire it in the ViewDidLoad you will never know when you will receive the response data and you can't be sure that the data will be ready by the time you view appear.
I think you have just 2 possibility:
The first one is to load the data in the previous view controller and then just pass the data that got ready to you view controller.
The second is to use a synchronous method (the findobject method maybe?) and put the call in a method that is fired BEFORE the view appear (like the viewWillAppear: method). But your view will stuck for a moment (I think) while the data is retreiving... However this second solution probably resolve your problem but using synchronous method to retrieve data from a slower data source is usually bad design solution.
D.

Resources