Activity Indicator Continues after Block - objective-c

got this code:
[self setQRCodeScannerMode:false];
[self.activityIndicator startAnimating];
TBXMLSuccessBlock successBlock = ^(TBXML *tbxmlDocument) {
if ([[TBXML elementName:tbxmlDocument.rootXMLElement] isEqualToString:#"xxxxxx"]){
[self setQRCodeScannerMode:true];
} else {
[self setQRCodeScannerMode:true];
}
};
The thing is that when I set my scan mode to true inside the method [setQRCodeScannerMode] I'm stopping the activity indicator.
But surprise!!! the activity indicator is still working and messing with my view after some seconds.
What can I do?

You need to stop the activity indicator in your completion block:
[self setQRCodeScannerMode:false];
[self.activityIndicator startAnimating];
TBXMLSuccessBlock successBlock = ^(TBXML *tbxmlDocument) {
if ([[TBXML elementName:tbxmlDocument.rootXMLElement] isEqualToString:#"xxxxxx"]){
[self setQRCodeScannerMode:true];
} else {
[self setQRCodeScannerMode:true];
}
[self.activityIndicator stopAnimating];
};
This assumes the completion block is called on the main thread. If there is no guarantee that the completion block is called on the main thread you can do this:
[self setQRCodeScannerMode:false];
[self.activityIndicator startAnimating];
TBXMLSuccessBlock successBlock = ^(TBXML *tbxmlDocument) {
if ([[TBXML elementName:tbxmlDocument.rootXMLElement] isEqualToString:#"xxxxxx"]){
[self setQRCodeScannerMode:true];
} else {
[self setQRCodeScannerMode:true];
}
if ([NSThread isMainThread]) {
[self.activityIndicator stopAnimating];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[self.activityIndicator stopAnimating];
});
}
};

You are supposed to call [self.activityIndicator stopAnimating]; after you are finished with it, if thats not working for you, try this!
[self.activityIndicator performSelector:#selector(stopAnimating) withObject:nil afterDelay:0.1];
Hope that Helps!

One thing that you can do is working with the indicator on a different thread i.e. not on the Main Thread. You can use NSOperationQueue and NSInvocationOperation

simply add this code after the block
[self.activityIndicator stopAnimating];

Related

Why NSTimer doesn't stop with invalidate?

I make a Cocoa application.I create a NSTimer,it can work,but can't stop with invalidate.The following is my code.Anyone else can help me?
#implementation Document
{
NSTimeInterval elapsedTime;
NSTimer *timer;
}
- (IBAction)start:(id)sender
{
elapsedTime = 0.002f;
dispatch_async(dispatch_get_main_queue(), ^{
if(timer == nil)
{
NSLog(#"Starting");
captureFrame = [[CaptureFrame alloc] init];//captureFrame is another object
timer = [NSTimer scheduledTimerWithTimeInterval:elapsedTime target:captureFrame selector:#selector(sendSingleImageImage) userInfo:nil repeats:YES];
NSThread *thread = [NSThread currentThread];
NSLog(#"Current thread is%#",thread);// I want to see the thread.
}
else
{
NSLog(#"Exist!");
[timer invalidate];
timer = nil;
}
});
[_stopButton setEnabled:YES];
[_startButton setEnabled:NO];
}
- (IBAction)stop:(id)sender
{
dispatch_async(dispatch_get_main_queue(), ^{
[timer invalidate];
timer = nil;
NSThread *sthread = [NSThread currentThread];
NSLog(#"S thread is %#",sthread);// I want to see the thread
});
[_startcastButton setEnabled:YES];
[_stopButton setEnabled:NO];
NSLog(#"Stopping");
}
I tried use the following method,but didn't work.
dispatch_async(dispatch_get_main_queue(), ^(void)block)
I also tried to find whether two actions in the same thread,and the result is yes.
So please tell me how to stop the NSTimer.
Any help is appreciated,thanks a lot in advance.

iOS7 UISearchDisplayController bug with Navigation Bar not automatically hidden

Reveal shows the following:
-> UINavigationBar
-> UINavigationTransitionView
->UITableView
->UISearchDisplayController
->UISearchBar
When I hit the searchBar the searchDisplayController does not hide the NavigationBar instead it reveals half of it.
I do not use auto layout (so not constraints), I am using a UITableViewController, I cannot use the new UISearchController and I am looking for a solution to either make it work as it should or either do not allow it to expand. The problem occurs only for the iPad. The project supports iOS7 and above.
I am trying to catch everything in the following methods:
#pragma mark - UISearchDisplayController Delegate
- (void)searchDisplayController:(UISearchDisplayController *)controller willHideSearchResultsTableView:(UITableView *)tableView {
self.onlineSearchRequestUUID = [NSUUID UUID];
dispatch_async(self.searchSerialQueue, ^{
if ([self.parentVC needsEmail]) {
self.capiContact->setFilter(CAPIContactFilter::WithEmail, "");
} else {
self.capiContact->setFilter(CAPIContactFilter::All, "");
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.parentVC.tableView reloadData];
});
});
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
dispatch_async(self.searchSerialQueue, ^{
[self filterContentForSearchText:searchString
scope:[controller.searchBar scopeButtonTitles][[controller.searchBar selectedScopeButtonIndex]]];
dispatch_async(dispatch_get_main_queue(), ^{
[controller.searchResultsTableView reloadData];
if ([searchString length] == 0) {
[self.parentVC.tableView reloadData];
}
});
});
return NO;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
dispatch_async(self.searchSerialQueue, ^{
[self filterContentForSearchText:[controller.searchBar text]
scope:[controller.searchBar scopeButtonTitles][searchOption]];
dispatch_async(dispatch_get_main_queue(), ^{
[controller.searchResultsTableView reloadData];
});
});
return NO;
}
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
if (![self.tableView isEditing]) {
searchBar.showsScopeBar = YES;
[searchBar sizeToFit];
_oldnavCol = [self.navigationController.view.backgroundColor copy];
#try {
[self.navigationController.view setBackgroundColor:[[[[self.parentVC.searchDisplayController searchBar] subviews][0] subviews][1] barTintColor]];
}
#catch (NSException *exception) {
LogDebug(#"%#", exception);
}
return YES;
}
return NO;
}
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar {
double delayInSeconds = .5;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0"))
{
[self.navigationController.view setBackgroundColor:_oldnavCol];
_oldnavCol = nil;
}
});
return YES;
}
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
//Here is where i have tried most of my tries to make it work with hacks but with no luck.
//self.navigationController.navigationBar.translucent = YES;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0")) {
self.searchDisplayController.searchBar.frame = CGRectMake(0, 0, self.tableView.frame.size.width, 88);
}
}
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
self.tableSearchText = searchText;
//some code here
if ([searchText length]) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSString *uuid = [self.onlineSearchRequestUUID UUIDString];
//some more code here
});
}
}
I don't think I am missing something.
Any help would be appreciated.
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
[[[parentViewController navigationController] navigationBar] setHidden: YES];
}
}
for my case parentViewController is either self or self.parentVC and it works like a charm.

Run method only after other

i need to make this methods:
1) find and remove matches
2) fill empty cells
3) do 1 and 2 while no matches found
4) only when no matches found - find reshuffle method
How i do this :
[self launchAsyncCheckAndFill:^{
[self isReshuffleNeeded];
}];
-(void)launchAsyncCheckAndFill:(void(^)(void))completionHandler {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self performSelectorOnMainThread:#selector(check) withObject:nil waitUntilDone:true];
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler();
});
});
}
- (void) check {
[self performSelector:#selector(fillEmptyCells) withObject:nil afterDelay:0.4];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
if ([self checkMapForMatchesWithFindMatchesMode:NO isCopyVector:NO]) {
[self check];
}
});
}
First of all i'm remove matches, then i go this methods : first of all i go launchAsyncCheckAndFill method, that on call back must start reshuffle method.
launchAsyncCheckAndFill must send callback only when check method is done. Check method is done in loop, this method must always fillEmptyCells, then checkmatches and while matches not found, only then must work callback on launchAsyncCheckAndFill .
But now behavior, that in loop always removing match, but method on reshuffle works after first remove and fill.
btw time 0.4 , 0.5 it's for animation, for this time figures moves and changes position. unfortunately in cocos i can only use
[elem.sprite runAction:[CCMoveTo actionWithDuration:0.4f position:elemPos1]];
that put position only after delay.
UPDATE:
[self launchAsyncCheckAndFills];
[self isReshuffleNeeded];
-(void)launchAsyncCheckAndFills {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{
[self check];
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
}
- (void) check {
[self performSelector:#selector(fillEmptyCells) withObject:nil afterDelay:0.4];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
if ([self checkMapForMatchesWithFindMatchesMode:NO isCopyVector:NO]) {
[self check];
}
});
}

Problem with UIImageView loading via OperationQueue

I load a UIImageView using an NSOperationQueue.
The load fetches an image from the Internet and then adds it to an image view. The problem I have is that the method finishes but it takes about 3 seconds later for the image view to actually either show the image or remove the image view from the superview...
- (void)viewDidLoad { NSLog(#"AirportDetailView: viewDidLoad");
[super viewDidLoad];
[self.activity startAnimating];
self.queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(loadImage) object:NULL];
[self.queue addOperation:operation];
[operation release];
}
-(void)loadImage {
[myAp ImageForAp];
NSLog(#"ImageFor Ap Ended, %#",myAp.ApDiagram);
[self.activity stopAnimating];
if (myAp.ApDiagram==NULL) {
NSLog(#"Finally Gets Here");
[self.Diagram removeFromSuperview];
}
else {
NSLog(#"Finally Gets Here with Diag");
[self.Diagram setBackgroundImage:myAp.ApDiagram forState:UIControlStateNormal];
}
}
The NSLOG shows a delay between the first two log statements of about 3 seconds can't understand why....
Updated with my Latest Code......
-(void)loadImage {
[myAp ImageForAp];
NSLog(#"ImageFor Ap Ended, %#",myAp.ApDiagram);
[self performSelectorOnMainThread:#selector(UpdateUI) withObject:nil waitUntilDone:NO];
}
-(void)UpdateUI {
[self.activity stopAnimating];
if (myAp.ApDiagram==NULL) {
NSLog(#"Finally Gets Here");
[self.Diagram removeFromSuperview];
}
else {
NSLog(#"Finally Gets Here with Diag");
[self.Diagram setBackgroundImage:myAp.ApDiagram forState:UIControlStateNormal];
}
}
Make sure that the loadImage method is being run on the main thread. All UI operations need to happen on the main thread to work as expected. Your code will need to look similar to this.
-(void)loadImage {
[myAp ImageForAp];
[self performSelectorOnMainThread:#selector(UpdateUI) withObject:nil waitUntilDone:NO];
}
-(void)UpdateUI {
[self.activity stopAnimating];
if (myAp.ApDiagram==NULL) {
[self.Diagram removeFromSuperview];
}
else {
[self.Diagram setBackgroundImage:myAp.ApDiagram forState:UIControlStateNormal];
}
}
There is also a possible memory leak inside of viewDidLoad.
self.queue = [NSOperationQueue new]; should be changed to
self.queue = [[[NSOperationQueue alloc] init] autorelease];

Activity Indicator doesn't spin

I'm trying to add a spinning activity indicator (UIActivityIndicatorView) to my app while it parses data from the internet. I have an IBOutlet (spinner) connected to a UIActivityIndicatorView in IB. Initially I had it set up like this:
-
(void) function {
self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhite];
self.spinner.hidesWhenStopped = YES;
[spinner startAnimating];
//parse data from internet
[spinner stopAnimating];}
But the spinner wouldn't spin. I read that it had something to do with everything being on the same thread. So I tried this:
- (void) newFunction {
self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhite];
self.spinner.hidesWhenStopped = YES;
[spinner startAnimating];
[NSThread detachNewThreadSelector: #selector(function) toTarget: self withObject: nil];
[spinner stopAnimating];}
But still no luck. Any ideas? Thanks.
Your newFunction: method should look like this:
- (void) newFunction {
self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
self.spinner.hidesWhenStopped = YES;
[NSThread detachNewThreadSelector: #selector(function) toTarget: self withObject: nil];
}
And your function method should look like this:
- (void) function {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self.spinner performSelectorOnMainThread:#selector(startAnimating) withObject:nil waitUntilDone:NO];
//...
[self.spinner performSelectorOnMainThread:#selector(stopAnimating) withObject:nil waitUntilDone:NO];
[pool drain];
}
you should not intitialize indicator again .please replace your code with this.
-(void) function {
[spinner startAnimating];
[self performSelector:#selector(newfunction) withObject:nil afterDelay:3.0];
}
- (void) newfunction {
[spinner stopAnimating];
}
Thanks.
Just see that the "//parse data from internet " is synchronous or asynchronous. Asynchronous would mean that a separate thread would start from that point on, and the current function execution will continue without delay.
In your second example, you are explicitly making separate thread, which means that #selector(function) will happen on a separate thread, and the next statement [spinner stopAnimating] is executed immediately. So, it seems like spinner is not spinning at all.
Moreover, make sure you start and stop the activity indicator on main thread only.