I have a UITableView that requests for more data from the server when the user hits the bottom of the table (similar to the Twitter application). However, I'm trying to use a modal segue to filter out data to the user's desire. In order to properly select which data to filter, I have to load ALL of the data to categorize it. In order to load everything, I am required to send out multiple NSURLConnections to load multiple pages. I am trying to have it so when one completes, the next one starts.
However since the connection completes with connectionDidFinishLoading, I have not figured out a way to send out simultaneous NSURLConnections from within prepareForSegue. I tried using a while loop in prepareForSegue as follows:
while (All of the data is not loaded) {
if (isLoading == NO) {
[self loadMoreResults];
}
}
where "isLoading" is a BOOL declared in my viewcontroller's implementation file. isLoading changes value to YES inside loadMoreResults, and changes back to NO at the end of connectionDidFinishLoading. However, within prepareForSegue, isLoading never changes back from YES to NO.
Is this a multithreading issue? I have done research on other questions and see that NSURLConnection has a class method sendAsynchronousRequest:queue:completionHandler: where the completion handler might help, but I'm unsure how I would use it.
ALSO: I want to continue executing prepareForSegue AFTER the last connection finishes, not right after it sends the request.
Thanks in advance!
Then you should not link the segue directly from the bar button item to the view controller in the storyboard.
Just link a general segue with identifier from the table view controller to the filter view controller.
And from the bar button item, create an action for it from the storyboard so that you can send out multiple NSURLConnections first.
Finally then in your code, after the last connection finishes, call the performSegueWithIdentifier method.
Related
Here is the scenario:
OrderVC have a table view on which if you right swipe it shows some
options. In which one of the option is Checkout.
When user taps on Checkout it opens up CheckoutVC which has a parent class OrderVC.
Here user can add some text and can attach multiple images and can also save this data as draft which is achieved using core data. But when user submit the bill I'm using AFNetworking to call web api and upload images using AFMultipartFormData. All of this process is taking place on a background thread i.e.dispatch_async
I can't update UI in dispatch_get_main_queue because methods are calling other method from within see this question it'll clear this point. So it calls the update UI right after first method is finished.
Question
As long as background thread is working it should show activity indicator on the cell. When it's finished and the response is success in CheckoutVC it should reload the tableView of OrderVC.
Solution I triedI tried to run a for loop in allOrderID which are the ID's I get via web api hit of active orders. Then I made a call to MR_findFirstByAttribute to find if any of the fetched OrderID exist in drafts. There is an attribute isSending in DraftOrderInfo entity which is a BOOL and I truns it to true when checkout enters background thread. So if isSending is true I show the activity indicator in place of a UIView I created.
for (NSString *orderID in allOrderId) {
DraftOrderInfo *dpi = [DraftOrderInfo MR_findFirstByAttribute:#"orderID" withValue:orderID];
if (dpi.isSending) {
orderCell.rightUtilityButtons = nil;
activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityView.center = CGPointMake(orderCell.orderStatusIndicatorBadgeView.frame.origin.x-2, orderCell.orderStatusIndicatorBadgeView.frame.origin.y-8);
[activityView startAnimating];
[orderCell.orderStatusIndicatorBadgeView addSubview:activityView];
}
}
The output I get is that when OrderVC is loaded it started showing activity indicator on all the cells.
First, your following point is not valid. Read the comments on the question:
can't update UI in dispatch_get_main_queue because methods are calling
other method from within see this question it'll clear this point. So
it calls the update UI right after first method is finished.
Back to the problem. There is multiple good practices to use. One of them:
Add the UIActivityIndicator to the UITableViewCell at design time(nib or storyboard) as done with the UILabel(s) and other controls.
On submitting the checkout. Change the isSending status to YES and inform the parent UIViewController to reload the table data by calling reloadData method on the UITableView.
In cellForRowAtIndexPath or willDisplay:forRowAt method set the state of the activity indicator as animating or stopped based on the isSending value. This way even if reloading the table or scrolling up and down, the activity indicator will have its state right.
Whe the submitting finishes. Change the isSending state to YES and inform your parent ViewController to reload the table by calling reloadData method on the UITableView. And since the checkin finishes in a background thread you should inform your parent ViewController using dispatch_get_main_queue. read the comments on the question you added to your question. This point you mention regarding the dispatch_get_main_queue is wrong.
in a ViewController, that hosts a TableView, I load the data for the table view using AFNetworking in viewDidLoad.
This can take a couple of seconds. If during the network call, the users swipes back to the previous VC, the app crashes. I think it is because the network call is still running, comes back and the original callback for the network call is somehow lost?
I started using stuff like "userInteractionEnabled = false" and setting it back to true when the data finished loading or setting a global variable in viewWillDisappear and checking this in the callback. This works but seems quite wrong.
What is the proper method/func to load data for a table view?
How is a situation like this properly handled, e.g. should I cancel all AFNetworking request in viewWillDisappear?
Thanks a lot for a hint!
This is how my application is looking now:
After I perform a database update in my detail controller in view number 7 in the image above as soon as the save button is clicked the details are saved the the database. I'm taken back to tableView number 5 and expect the associated row to show latest updates by calling a special method from the parse.com framework that reloads objects and refreshes the table view e.g. [self loadObjects].
I use an unwind segue. In view 7 I make a connection between the save button and the exit symbol of it's controller window in interface builder and then in tableView number 5 I have my segue method that corresponds to this connect.
Unwind segue method:
-(IBAction)saveDetailsButtonTapped:(UIStoryboardSegue *)segue {
// alert goes here
[self performSelector:#selector(didTapRefreshButton:) withObject:self afterDelay:1.0];
}
This method clears the table and loads the first page of objects:
- (IBAction)didTapRefreshButton:(id)sender {
[self loadObjects];
}
When save is clicked on view number 7 the details are saved to the db and user is bought back to table view number 5 then the method above runs after 1.0 delay. I thought this was ok but didn't feel too right. I tried it on my phone and sometimes the delay wasn't long enough, meaning a failed refresh.
I then decided to try using a UIAlertView delegate method to detect when the ok button of the alertview was pressed and it worked ok most times but then the times I pressed OK to dismiss the alert really quickly upon arriving back on the view and the data wasn't reloaded.
Is there a better solid reliable way to refresh my data?
I need some way of knowing that the database update was successful and only then run the [self loadObjects] method and maybe do that automatically.
I have two methods that detect when objects will load (e.g. like when a button has been tapped) and when they have loaded. I have put some spinner code in there to show a spinner while loading is happening and take it away once it's done.
Isn't there some sort of way to queue methods, like some how in one method make it so one thing doesn't happen until another thing has happened?
If so, I'd really appreciate some insight and examples as I could just mark the app as complete but even though I'm not being paid and it's charity work I still have the urge to do my best.
Thanks for your time.
Kind regards
I have put some spinner code in there to show a spinner while loading is happening and take it away once it's done.
You should do something like that here.
I need some way of knowing that the database update was successful and only then run the [self loadObjects] method and maybe do that automatically.
Because you're saving to parse, it should be the parse SDK that tells you when the save is complete. If you're saving in the background (which you should be) then use the save method when provides you with a callback block that is called when the save has completed. This block being called is your trigger to remove the spinner and segue.
Side note :-
Yes, there are several different kinds of queues, most better than using performSelector:..., but there are also other ways of working with asynchronous activities and you should look at the asynchronous activity for guidance. i.e. can I get a callback when this is done, rather than how long should I wait and hope that it is done.
I have a UIPageViewController that contains view controllers that are instantiated from data fetched over the network. When the user is paging and gets within a few pages of the end, I kick off a new network operation and put the resulting new view controllers onto the end of my UIPageViewControllerDataSource's mutable array of view controllers. In general this all works pretty well.
But if the network is slow (or the user is fast), it's possible to reach the final page before the network operation has completed. Since pageViewController:viewControllerAfterViewController: gets called when the user reaches the last page, it has already returned nil ("to indicate that there is no next view controller").
Is there a good way to force the UIPageViewController to call pageViewController:viewControllerAfterViewController: again (so that when the network operation completes and there are now new pages, I can force this call so the user can page to the new pages)? Or is there a workaround (maybe preventing the user from paging to the last page, but still showing the bounce animation)?
See here: Refresh UIPageViewController - reorder pages and add new pages
When you get your data back from the network, call setViewControllers: direction: animated: completion: and set the current view to the current one again. That forces it to re-call pageViewController:viewControllerAfterViewController:
ssloan's answer was part of the key, but in my case simply calling setViewControllers:direction:animated:completion: in the network operation completion block wasn't enough (due to some edge cases).
My solution was to store the most recent view controller for which pageViewController:viewControllerAfterViewController: returned nil (I called it endPage) and to create a method...
- (void)fixAndResetEndPage {
if (self.endPage && self.endPage == self.currentPage) {
[self.pageController setViewControllers:#[self.currentPage] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
self.endPage = nil;
}
}
...which I called whenever my NSMutableArray of content controllers was mutated (e.g., network operation completion) and in pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted: whenever completed was YES.
I want to start a wait animation/show UIAlertView before calling a segue so that the user will know they have to wait for a couple of seconds while the segue gets processed and the next ViewController gets populated with relevant data and shows up!
The problem is performSegueWithIdentifier prevents any kind of animation/UIAlertView to show up. Only after the segue finishes then the animations get their chance to popup!
How to solve this ?
You should be able to implement your animation logic in the prepareForSegue method. That method gets called and finished before the segue is executed.
Also, if this flow doesn't fit, fire off the performSegue method when the animation is over or the user responds to the UIAlertView that you have fired off.
OR if you haven't been using MBProgressHUD, take a look at that. I use this when I am dealing with external data and want the user to know something is going on (getting data from a web-service). It is easy and simple to implement.