Method tableView.reloadData() asynchronous execution - ios

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())

Related

Calling a function once after tableview.reloadData

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

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.

Un-Wire an event from being called

How can I unwire a function so it doesn't get called?
//Wire an event
myScrollView.delegate = self
... do something
// How can I unwire it so the scrollview functions dont get called?
Simply set the delegate property to nil if you no longer wish to have any of the delegate methods called.

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.

Notify table view to reload data

I have this table view controller:
class EventListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UIPickerViewDelegate, UIPickerViewDataSource {
// Event table view
#IBOutlet weak var eventTableView: UITableView!
var events: [Event] = []
...
I would like to load the data asynchronously from a web service, this can take up to 5 seconds.
I have this asynchronous code:
override func viewDidLoad() {
super.viewDidLoad()
...
// ApiClient is a custom wrapper for my API
ApiClient.sharedInstance.getEvents({
(error: NSError?, events: [Event]) in
// All this runs within an asynchronous thread
if let error = error {
println("Error fetching events")
println(error.localizedDescription)
}
self.events = events
// How to notify the table view?
})
...
The data loads fine, but the table stays empty. Once viewWillAppear(...) is called again, the data is in the table.
Do I need to notify the table view? What's the cleanest way / best practice?
Thanks!
Simply call self.eventTableView.reloadData().
If your code in your closure is executed on an asynchronous thread, you may need to encapsulate that call into a dispatch_async call so that it is triggered on the main thread (because all UI-related work must always be run in the main thread):
// All this runs within an asynchronous thread
...
self.events = events
// Notify the tableView to reload its data.
// We ensure to execute that on the main queue/thread
dispatch_async(dispatch_get_main_queue()) {
self.eventTableView.reloadData()
}
To refresh the tableView calling cellForRowAtIndexPath, you do:
self.eventTableView.reloadData()

Resources