I am using a NSFetchedResultsController to populate a UITableView. The fetch take some time so I would like to present a spinning wheel to the user while the fetch is under way.
How would I do that?
You should start your spinner in the Main thread and push the "heavy work" for a secondary thread. When the work is done, stop the spinner. You can achieve that with something like this:
// Start the spinning here.
[mySpinner startAnimating];
// Declare the queue
dispatch_queue_t workingQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// it's not, so we will start a background process to calculate it and not block the UI
dispatch_async(workingQueue,
^{
// Some heavy work here.
dispatch_async(dispatch_get_main_queue(), ^{
// stop the spinner here
[mySpinner stopAnimating];
});
});
Doing the following in the Main thread wont make you accomplish what you want:
Start Spinner => Heavy work => Stop Spinner
When the Heavy work begins, it will block your UI thread, so you won't actually see the UIActivityMonitor animating.
To finish I would advise you using this as spinner.
You have several way to do that :
had a UIBarItem if you use UINavigationBar and set customView to UIActivityIndicator at the beginning of your function and hide it a the end.
Create a "modal" view with a UIActivityIndicator at the center of the view, and add this subview to your table view and remove it a the end of your function.
formally just you can use the UIActivityIndicatorView to present the wheel and for some good and delicate way use the MBHoods from here download the demo app here
Related
I have this application in which I have a right bar button item which adds the search bar as title view. This works fine but when the table view is scrolling, this doesn't happen. It only adds when the table view has stopped scrolling.
Any way to handle this?
The main thread does not responds to events if its busy, use GCD to add this to Queue
Check if this function is called while scrolling. If yes , then simply using GCD will solve your problem.
-(void)userTapOnRightBarButton{
NSLog(#"Main thread response to touch during scrolling"):
dispatch_async(dispatch_get_main_queue(), ^{
[self addSearchBar];
});
}
In my app I have two methods to hide and show a subview of a view controller called
-(void)hideCurrentView
-(void)showCurrentView
They are always called together. When calling hideCurrentView I want to start showing a loading screen and then hide this loading screen when showCurrentView has shown all necessary ui elements. My loading screen is a view with an activity indicator as a subview. The problem is that I have to do all the actions that happen in hideCurrentView and showCurrentView on the main thread. That is why my loading screen will never show up. :-(
I tried to put the show loading screen method to a background thread:
-(void)hideCurrentView {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, (unsigned long)NULL), ^(void) {
[self showLoadingScreen];
});
[self performSelectorOnMainThread:#selector(hideCurrentViewOnMainThread) withObject:nil waitUntilDone:YES];
}
-(void)showCurrentView {
[self performSelectorOnMainThread:#selector(showCurrentViewOnMainThread) withObject:nil waitUntilDone:YES];
}
That does not work. The loading screen never shows up because the main thread is blocked. I also tried showing the loading screen directly on the main thread but it doesn´t work too.
Can anyone tell me hoew to show the loading screen before doing the ui actions and then hide it again when all ui actions have finished?
Thanks for your help!
You should not call showLoadingScreen in a background thread if you are just adding a subview. (or more generally manipulating the UI)
I'm guessing you have something in your hideCurrentViewOnMainThread that is not UI related and blocks the main thread and it's that part of the code that should be run in another thread.
I'm facing a strange issue about the animation of the UIActivityIndicatorView.
I have a splitViewController where I have a UITableView on the left side, and another one on the right side.
When I enter the application I am doing some operations in background to load datas and each UITableViewCell of the tables will contains a UIActivityIndicatorView.
The operations that have to be executed on the left side are on a queue. The operations to execute on the right side are on a different queue.
The right side operations have major priority respect the operations that have to be executed on the left side.
When the right side queue start, The UIActivityIndicatorView is placed in each UITableViewCell of the table and starts immediately.
When the left side queue start, The UIActivityIndicatorView is placed in each UITableViewCell of the table but does not start the animation immediately. (I have to scroll the table to force the animation).
How can I solve this situation?
How can I animate the UIActivityIndicatorView on the other table without having to scroll the table?
I don't know what kind of thread you use to run operation in background but if effect to interface (UIActivityIndicatorView in your situation) my recommendation for you is using the following code to Run UI and load operation thread
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//Background Thread
dispatch_async(dispatch_get_main_queue(), ^(void){
//Run UI Updates
});
});
Ok, so I have this piece of code:
- (void) searchBarSearchButtonClicked:(UISearchBar *)aSearchBar
{
[_activity startAnimating];
[self.searchBar bringSubviewToFront:_activity];
[self.searchBar resignFirstResponder];
if([_activity isAnimating]){
NSLog(#"its animating!");
}
dispatch_queue_t background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(background, ^{
[self performSelectorOnMainThread:#selector(filter:) withObject:aSearchBar.text waitUntilDone:YES];
dispatch_async(dispatch_get_main_queue(), ^{
[_activity stopAnimating];
if(![_activity isAnimating]){
NSLog(#"its not animating anymore!");
}
[self.tableView reloadData];
});
});
}
what i'm trying to do is start an activity indicator which I added as a subview to the search bar. In this code, I start the animation (logging for test) on the main thread, then set up a background asynchronous thread which runs the method that generates the new data set which will reload the tableview. I wait for this to return, and which point, I stop animation and reload the tableview on the main thread.
the result is this...when the application runs, I go to the table view, and I type a word in the search bar. When I hit enter, the activity indicator shows up, the table loads, and the activity indicator stops. This is great.
Then, I click on the search bar again, start a new search, only this time, the activity indicator does not show itself, but the NSLog shows that it IS animating. The tableview reloads as expected, but no activity indicator on the UI, but based on the NSLog, it seems to working.
Finally, if I wait about a minute, and search again, it works fine. What I'm suspecting is that that the background thread is not completing maybe, and the main thread continues doing what it needs to do.
Honestly, I've been beating my head for two days now, can someone explain what I'm doing wrong?
There's a chance that this has something to do with the view hierarchy, depending on where you added the UIActivityIndicatorView to. If you added it directly to the search bar, it might reorganize the subviews in a way you don't expect (and move the activity indicator behind some of them, blocking it from being visible, even though it's animating.)
A good option might be to embed the search bar within a toolbar, and then have the activity indicator on the left (or right) with flexible space separating it from the search bar.
I am trying to set a custom UIView class's background color. The class also does quartz drawing in the drawRect:method.
Since background color change does not take place until the next redraw of the view, I change the UIView's backgroundColor property before calling setNeedsDisplay. I have set a UIActivityIndicatorView to animate while the view is redrawing.
self.backgroundColor = theColor;
[indicator startAnimating];
[self performSelectorInBackground:#selector(setNeedsDisplay) withObject:nil];
The indicator is stopped at the end of setNeedsDisplay. theColor will change every time I need to call this.
Let's say I have a time consuming setNeedsDisplay process. I would like to set the background and keep the indicator animation. Currently, changing backgroundColor calls setNeedsDisplay but doesn't even change the backgroundColor until the performSelectorInBackground method runs! Therefore my app hangs and no indicator is ever animated.
How do I deal with this ordering problem? Thanks.
Edit: I meant that my drawrect: may be time consuming.
Let's say I have a time consuming setNeedsDisplay process
Let's not. You have no business overriding setNeedsDisplay. I am not at all clear on what you're ultimately trying to accomplish but this entire question seems to be a misunderstanding of how to draw. When you call setNeedsDisplay (which, as you've been told, you must do in the main thread), that's that; you stand out of the way, and when the redraw moment comes, your view's drawRect: is called. That's drawing.
If the problem is simply that the activity indicator never gets going, that's because you never give it a chance. It too is not going to start going until the redraw moment. But you are stopping the activity indicator before the redraw moment even comes! So obviously you'll never see it go.
The way to start an activity indicator visibly before the next thing you do is to step out to the main thread after the next redraw moment. This is called "delayed performance". Example:
self.backgroundColor = theColor;
[indicator startAnimating];
double delayInSeconds = 0.1;
dispatch_time_t popTime =
dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// do something further, e.g. call setNeedsDisplay
};
You could extend that example by calling dispatch_after yet again to stop the indicator after the next redraw moment.
I must impress upon you, however, that if the mere act of drawing takes so long that you need an activity indicator to cover it, you're drawing wrong. Your act of drawing must be very very fast. You might want to watch the WWDC 2012 video on this very topic; it gives excellent tips on how to draw efficiently.
You can update UI only on Main thread, not in backgroung
Try to use another subview with activity indicator, put in on before redraw and remove from superview after