I have an UITableView with some UITableViewCells and when I click on a specific cell the application will download some information from my server. The question is: "How can I show IMMEDIATELY* a view with only an UIActivityIndicator which is animated during all the time of the download and stops its animation when the download is complete?"
*Note: It should not be after other strange operations from the app, it must be the first thing after the click on the cell.
You can use the below method which starts and stops the activity indicator on main thread in a single method
- (void)showIndicatorAndStartWork
{
// start the activity indicator (you are now on the main queue)
[activityIndicator startAnimating];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do your background code here
dispatch_sync(dispatch_get_main_queue(), ^{
// stop the activity indicator (you are now on the main queue again)
[activityIndicator stopAnimating];
});
});
}
Note:
I am doing some background stuff so I have used dispatch_async, if you also want to download something in background you can also use dispatch_async else you can also download the stuff on main thread.
Related
In my iOS app (a kind of flashCard application) I'm using a UIWebView and once the webview content loading is finished I need to perform some UI operations (changes).
I'm checking for this in webViewDidFinishLoad.
When a user taps on a card it will flip and different content is gets loaded. I am using the code below in this flipAction as well as in swipeAction (when user moves from one card to another) to check:
if (![[myWebView stringByEvaluatingJavaScriptFromString:#"document.readyState"] isEqualToString:#"complete"])
{
[self performSelector:#selector(myCustomMethod:) withObject:self afterDelay:3.0];
}
Sometimes, not always, my UI will freeze on the above if condition and after that the UI will not respond further. The app must be manually killed and relaunched.
Do I need to call stringByEvaluatingJavaScriptFromString: method other than thread?
or what may be the cause for this?
You can try background thread
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
dispatch_async(queue, ^{
// async operation
// Call your method here
dispatch_sync(dispatch_get_main_queue(), ^{
// Update UI here
});
});
When i tap on my button, my function was called
[myBtn addTarget:self action:#selector(myFunction) forControlEvents:UIControlEventTouchUpInside];
In my function, a collection of complex statement will be executed and take a litte bit time to run, so i want to show Loading (UIActivityIndicatorView) as the following:
-(void) addTradeAction {
//Show Loading
[SharedAppDelegate showLoading];
//disable user interaction
self.view.userInteractionEnabled = NO;
//execute call webservice in here - may be take 10s
//Hide Loading
[ShareAppDelegate hideLoading];
}
When tap on myBtn (my Button) -> after 3s or 4s, [ShareAppDelegate showLoading] was called.
It is unusual when i use [ShareAppDelegate showLoading] on other Function, -> it work very nice, i mean all the statement be executed in order.
All i want, when i tap on My Button, Loading will be called immediatelly.
Tks in advance
A correct way to perform a tasks in background, and in your case showing an activity indicator, is :
-(void)myBackGroundTask
{
//here showing the 'loading' and blocking interaction if you want so
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//here everything you want to perform in background
dispatch_async(dispatch_get_main_queue(), ^{
//call back to main queue to update user interface
});
});
}
With this kind of block, you are sure that your interface do not freeze, and keep a smooth animation.
If your complex statements do not any UI animations or UI related code, then you can execute that part in a different thread(other than the mainThread). Once the statements are done(or in completion block), you can remove the loadingOverlay there.
Put myFunction to run on a background queue as it probably makes the system hang:
- (void)myFunction {
dispatch_queue_t myQueue = dispatch_queue_create("myQueue", NULL);
// execute a task on that queue asynchronously
dispatch_async(myQueue, ^{
// Put the current myFunction code here.
});
}
I have a class that generates images to be printed by the user. These images are created using QuartzCore (and some UIKit elements) and need to be run on the main thread.
In the view visible to the user while the images are being generated, I have a progress bar. This view is the delegate of the class that does the printing, and a method is called by the printer on the view to update the progress bar. The problem is, the progress bar doesn't visibly update until the printer is finished because the printer is clogging up the main thread. I can't move the progress bar off of the main thread because all UI updates must be done on the main thread.
I'm fairly new to multithreading; do I have any other options or must I go with an activity indicator or something similar?
Update your code so that the image creation is done on a background thread. This should be safe.
Then you can make calls onto the main thread to update the progress bar.
You can use GCD, Raywenderlich Tutorial
- (void)generatePage
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
/*
Here you can generate page and update progress
For example:
*/
[self updateProgress:10.0f];
sleep(1000);
[self updateProgress:20.0f];
sleep(3000);
[self updateProgress:100.0f];
});
}
- (void)updateProgress:(float)progress
{
dispatch_async(dispatch_get_main_queue(), ^{
progressView.progress = progress;
});
}
In my application i am using back ground thread for hitting multiple service and perform operation with core data. I have used main thread for back ground process ,Its working fine.
Here is my code
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(main,
^{
[self backGroundCall];
});
-(void)backGroundCall
{
NSLog(#"Done");
if([CacheManager refreshDBforFirstTimeUseWithDelegate:self])
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"IsDBInitialized"];
ContainerViewController *containerViewControllerInstance = [ContainerViewController getContainerInstance];
[containerViewControllerInstance setUserId:_userID];
[progressView setHidden:YES];
[self.view setUserInteractionEnabled:YES];
[self.navigationController setDelegate:containerViewControllerInstance];
[self.navigationController pushViewController:containerViewControllerInstance animated:YES];
}
}
once i initialize the data base , i need to navigate to the container view.During the initialization i will display one progress bar. That is working fine, when the entire background process is completed(app is in minimized state). During the background process if i come to the foreground progress bar is not showing at that time black screen is display instead of progress view . After the completion of the main threat container view all not display[if i comes to foreground of main thread process].
i need to show the progress bar, if i come back to the app in the middle of the main thread process. Please guide me to fix this issue.
Thanks.
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(main,
^{
[self backGroundCall];
});
This is a bit misleading... You call the method backGroundCall, but you are actually doing this on the main thread. If you want to make some operation on a working thread, you can do this:
// Declare the queue
dispatch_queue_t workingQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(workingQueue,
^{
// My background job
dispatch_async(dispatch_get_main_queue(),
^{
// Update the UI
}
);
});
I have a button on the currently navigated to viewcontroller, connected to an IBAction.
In the IBAction I create a UIActivityIndicatorView as usual, with [self.view addSubView], then load some pictures.
I've tried setNeedsDisplay on the indicator view, the view controller, and the window, but it still loads the pictures before showing the indicator, which of course is quite useless to me.
So I'm looking for a way to either force an instant redraw (which when I think a little more about it is unlikely to make work), or a way to load the pictures after the indicator has appeared, or a way to launch a separate thread or similar to start animating / show the indicator, or put the indicator in a separate viewcontroller and somehow force it to add/show itself before going on to the picture-loading.
Recommendations?
What I do in this situation is spawn a new thread, which frees up the main thread to handle UI interaction while stuff is loading in the background.
First show the UIActivityIndicatorView, then spawn a new thread that loads the images, then on the last line of the method that is executed in the new thread, hide the UIActivityIndicatorView.
Here's an example:
//do stuff...
[activityIndicatorView startAnimating];
[NSThread detachNewThreadSelector:#selector(loadImages) toTarget:self withObject:nil];
In your loadImages method:
- (void) loadImages {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//load images...
[activityIndicatorView performSelectorOnMainThread:#selector(stopAnimating)];
[pool drain];
}