In my secondView I do this:
-(void)viewDidDisappear:(BOOL)animated
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dbq = [[dbqueries alloc] init];
[[NSNotificationCenter defaultCenter] postNotificationName:#"abc" object:nil];
//disabling a button and turning a progressView in the mainView on
dispatch_async(queue, ^{
//WORK
});
}
The notification disables a button and causes a progressView(in the progressView there is an ActivityIndicator) on the mainView to start.
In the area, where work is done, a method is called 3 to 6 times and if they are all done, I send another notification to the mainView to enable the button and to stop the progressView.
Now the weird thing is, that the button gets enabled at the right time but the progressView stops randomly after a few seconds
- (void)enableStartButton
{
NSLog(#"enableStartButton");
self.startARButton.enabled = YES;
[self.progressView setHidden:YES];
[[self.progressView.subviews objectAtIndex:0] stopAnimating];
}
I really don't know whats going on here, thanks in advance!
Related
I have a UIViewController with button that brings another UIViewController. with clicking on button , as shown in my NSLog, and when this is done, I want to send a notification to load another viewcontroller . Well, although it seems everything is done right, somehow it does not work and the UIViewController not appear. Here is the code:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(infoPage:)
name:#"InfoPage" object:nil ];
-(void) infoPage:(NSNotification*)notification
{
NSLog(#"Code executing in Thread %#",[NSThread currentThread] );
InfoCtrol *i = [[InfoCtrol alloc] init];
i.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:i animated:YES];
}
my tabbaritem button
-(void)info {
[[NSNotificationCenter defaultCenter] postNotificationName:#"InfoPage"
object:nil
userInfo:nil];
NSLog(#"test not");
}
I think my problem is that: It's not in a mainThread but I do n't know how should I solved that:
I also used this but it didn't bring the UIViewController:
[self performSelectorOnMainThread:#selector(test) withObject:nil waitUntilDone:NO];
-(void)test{
[[NSNotificationCenter defaultCenter] postNotificationName:#"InfoPage"
object:nil
userInfo:nil];
}
If I just put this code in button, it displays the UIViewController, but I want to use NSNotificationCenter
InfoCtrol *i = [[InfoCtrol alloc] init];
i.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:i animated:YES];
My Log:
Code executing in Thread <NSThread: 0x1fd7c7e0>{name = (null), num = 1}
Update:
How should i remove last thread from mainThread
I don't know why you want to use a notification here, when you can perform the action directly without issue. But a simple thing you can do in notification methods that need to update UI is to just have them call themselves on the main thread if they're not already running on that thread:
-(void)myNotificationMethod:(NSNotification*)note {
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:#selector(myNotificationMethod:)
withObject:note
waitUntilDone:NO];
return;
}
// ... do some UI stuff
InfoCtrol *i = [[InfoCtrol alloc] init];
[self.navigationController pushViewController:i animated:YES];
}
I have 3 screens on my app.First is login. Second is search and third is process the task.
On login i retrieve data from a web service. It returns data in XML format. So the data is considerably large. So i am doing that task on a background thread like this to stop Mainthread freezing up on me:
-(BOOL)loginEmp
{
.....some computation
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
[self getAllCustomerValues];
});
}
-(void)getAllCustomerValues
{
....more computation.Bring the data,parse it and save it to CoreData DB.
//notification - EDIT
NSNotification *notification =[NSNotification notificationWithName:#"reloadRequest"
object:self];
[[NSNotificationCenter defaultCenter] postNotification : notification];
}
//EDIT
//SearchScreenVC.m
- (void)viewDidLoad
{
....some computation
[self.customerActIndicator startAnimating];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(stopActivityIndicator)
name:#"reloadRequest"
object:nil];
}
- (void)stopActivityIndicator
{
[self.customerActIndicator stopAnimating];
self.customerActIndicator.hidesWhenStopped = YES;
self.customerActIndicator.hidden =YES;
NSLog(#"HIt this at 127");
}
So on condition that login was successful, i move to screen 2. But the background thread is still in process( i know because i have logs logging values) . I want an activity indicator showing up here (2nd screen)telling user to wait before he starts searching. So how do i do it?How can i make my activity indicator listen/wait for background thread. Please let me know if you need more info.Thanks
EDIT: so I edited accordingly but the notification never gets called. I put a notification at the end of getAllCustomerValues and in viewDidLoad of SearchScreen i used it. That notification on 2nd screen to stop animating never gets called. What is the mistake i am doing.?Thanks
EDIT 2: So it finally hits the method. I dont know what made it to hit that method. I put a break point. I wrote to stop animating but it wouldn't. I wrote hidesWhenStoppped and hidden both to YES. But it still keeps animating.How do i get it to stop?
Ok, if it is not the main thread, put the following in and that should fix it.
- (void)stopActivityIndicator
{
if(![NSThread isMainThread]){
[self performSelectorOnMainThread:#selector(stopActivityIndicator) withObject:nil waitUntilDone:NO];
return;
}
[self.customerActIndicator stopAnimating];
self.customerActIndicator.hidesWhenStopped = YES;
self.customerActIndicator.hidden =YES;
NSLog(#"HIt this at 127");
}
Could you put your background operation into a separate class and then set a delegate on it so you can alert the delegate once the operation has completed?
I havent tried this, its just an idea :)
You could use a delegate pointing to your view controller & a method in your view controller like:
- (void) updateProgress:(NSNumber*)percentageComplete {
}
And then in the background thread:
float percentComplete = 0.5; // for example
NSNumber *percentComplete = [NSNumber numberWithFloat:percentComplete];
[delegate performSelectorOnMainThread:#selector(updateProgress:) withObject:percentageComplete waitUntilDone:NO];
My application performs a series of processing methods once a day or when a local notification is received to perform the series of tasks and is working as expected.
I tried adding an ActivityIndicator to a view that begins animating when a notification sent from the AppDelegate to perform the tasks is received. However all that happens is the screen darkens and none of the processing is done and the activity indicator doesn't appear. The indicator and subview that darkens the screen is used elsewhere in the app and is definitely working. I think it must be something to do with the notifications causing the app to hang and not proceed to call any of the other methods so it never completes.
This is the notification in the app delegate which is called just before the daily processing methods are called to start the activity indicator in a view controller, and the second one notifies the view controller to remove the subviews.
This method is in the AppDelegate;
- (void)GoForRankingAndSMProcessing{
[[NSNotificationCenter defaultCenter] postNotificationName:#"ranking" object:self];
RankingAndSMProcess *process = [RankingAndSMProcess alloc];
[process DoRankingAndSocialMediaProcessing];
[process release];
[[NSNotificationCenter defaultCenter] postNotificationName:#"rankingDone" object:self];
}
Which is picked up in another view, with the notifications setup in viewDidLoad.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(workingWhileRanking:) name:#"ranking" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(workingFinishedRanking:) name:#"rankingDone" object:nil];
And those call these methods in that view.
-(void)workingWhileRanking:(NSNotification *) notification{
loading = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
loading.opaque = NO;
loading.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.6f];
indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[indicator setHidesWhenStopped:YES];
indicator.center = self.view.center;
[indicator startAnimating];
[self.view addSubview:loading];
[self.view addSubview:indicator];
}
-(void)workingFinishedRanking:(NSNotification *) notification{
[indicator stopAnimating];
[indicator removeFromSuperview];
[indicator release];
[loading removeFromSuperview];
[loading release];
}
The notifications must be sent as the translucent subview appears, however none of the processing methods seem to be called so the view is permanently locked out until I force close the app.
Does sending the notification change the current flow of the applications launch sequence thereby ceasing to continue executing code in a class? Or have I gone about trying to add an activity indicator to show during some behind the scenes processing the complete wrong way?
If I am not interpreting incorrectly your code:
- (void)GoForRankingAndSMProcessing{
[[NSNotificationCenter defaultCenter] postNotificationName:#"ranking" object:self];
RankingAndSMProcess *process = [RankingAndSMProcess alloc];
[process DoRankingAndSocialMediaProcessing];
[process release];
[[NSNotificationCenter defaultCenter] postNotificationName:#"rankingDone" object:self];
}
your DoRankingAndSocialMediaProcessing is the task you would like to execute while the activity indicator is showing.
I assume that all processing happens on the same thread (UI thread), and this means that when you execute DoRankingAndSocialMediaProcessing basically your run loop (and hence, the UI) blocks and it is not updated any more. This should explain your issue.
You have several options to change this behavior, depending on what you do in DoRankingAndSocialMediaProcessing.
If you do not do anything related with the UI, it would be very easy to execute that method on a background thread by replacing the direct call to DoRankingAndSocialMediaProcessing with:
[process performSelectorInBackground:#selector(DoRankingAndSocialMediaProcessing) withObject:nil];
Of course you would then send the rankingDone notification from within DoRankingAndSocialMediaProcessing to make things work correctly.
If you can afford a background thread (i.e., you are not updating the UI from within DoRankingAndSocialMediaProcessing) this is the best option, because it will not block the UI at all.
hello i created a nsnotificationcenter in appdelegate.when the download is finished it sends a notification. i have a activity indicator on my class and i what to hide it when the download is finished but it doesn't work. i can see my nslog but it doesn't hide the acitivyindicator.
here is my code:
viewdidload:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(refreshView:) name:#"loadingFinished" object:nil];
my function:
-(void)refreshView:(NSNotification*)notification
{
activity.hidden = YES;
self.view.userInteractionEnabled =YES;
NSLog(#"alles geladen zeiger wird geladen");
}
if you are at any other thread, then it will not work cz UI not work in inner thread. use this
-(void)refreshView:(NSNotification*)notification
{
dispatch_async(dispatch_get_main_queue(), ^{
activity.hidden = YES;
self.view.userInteractionEnabled =YES;
});
NSLog(#"alles geladen zeiger wird geladen");
}
A notification callback may not be called on the main thread, but all your UI updates must be done in the main thread. So on your notification callback, use [self performSelectorOnMainThread:#selector(refreshViewFromMainThread)], and declare a new method:
- (void)refreshViewFromMainThread
{
activity.hidden = YES;
self.view.userInteractionEnabled = YES;
}
I have an application with multiple views that transitions to the main screen depending on which button was pressed. My current problem is that if the view is in the middle of animating then when the user selects another button then the whole layout becomes messed up. (ex: the views don't align with the screen meaning that they become a few pixels off)
What I would like to know is if there is a way to check if the view is currently animating and if so just have it animate to the last frame and skip anything in between. Below is a small piece of code that I have just tested based on what I have read on other user asked questions on SO:
-(IBAction)buttonPress:(id)sender
{
if([selectedView.layer.animationKeys count] > 0)
{
[selectedView.layer removeAllAnimations];
}
// Perform other calculations once the animation has stopped
}
There are many ways to do this but....
If you are using block animations you could set a "isAnimating" flag when the animation starts and set it again in the completion block. You could check the bool from anywhere and handle cases as needed.
As for needing code to execute after an animation occurs, but
// animation code in some method...
[UIView animateWithDuration:1.0
delay: 0.0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
isAnimating = YES;
fooView.alpha = 0.0;
}
completion:^(BOOL finished){
isAnimating = NO;
[[NSNotificationCenter defaultCenter] postNotificationName:#"FooBeDone" object:nil userInfo:nil]
}];
-(IBAction)buttonPress:(id)sender {
if (isAnimating) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(doBar:)
name:#"FooBeDone"
object:nil];
// possibly disable button to prevent multiple taps?
} else {
[self doBar];
}
}
- (void)doBar {
// do what needs to be done when when the animation is over
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"FooBeDone" object:nil];
// possibly enable button again
}
edit: I added more code to show a possible notification method. Creating extended loops in your IBAction will lock the user interface until the loop finishes and you can get back to the main run loop, so it's highly advised to avoid it. Notifications should give you the same effect but allow your main run loop to continue unhindered.