I'm making a simple iPhone app whose sole function is to update a UIView forever (until it exits).
I tried this in applicationDidFinishLaunching and viewDidLoad:
while(1) {
// update view here
}
but that doesn't work- the app never finishes loading. I'm sure there's a simple solution to this, I just don't know what it its.
Also: ideally, this process should consume very little resources.
You can't have a while (1) statement like there, as it not allow viewDidLoad to return, and your app will never get any other calls such as tap processing, screen draw updates, etc.
In viewDidLoad, set up a timer task using:
updateTimer = [NSTimer scheduledTimerWithTimeInterval: kUpdateTimeInterval target:self selector:#selector(updateView) userInfo:nil repeats:YES];
And have a method called updateView that actually does the updating. Instead of updateView you could also use setNeedsDisplay which will trigger a call the -drawRect method of your view class, and do the actual drawing there.
What ends up happening now is that your viewDidLoad will set up a repeating task and at every kUpdateInterval, your view will be updated.
Instead of updating the view all the time, maybe you could just call its -setNeedsDisplay method when the data that you're displaying changes.
Related
My application needs to show a temporary message, so I created Toast like android myself. It works well for only one toast. I am using NSTimer to hide the message. If I display more than one toast the timer becomes a problem. Only the last added toast becomes hidden, others do not become hidden. How can I hide all toasts?
Code:
Remove function:
-(void)removeToast
{
NSLog(#"removed");
[self.view removeFromSuperview];
}
Timer start:
timer = [NSTimer scheduledTimerWithTimeInterval:(4.0f)
target:self
selector:#selector(xxxx)
userInfo:nil repeats:NO];
I wrote this in above in separate NSObject class and created an object. I need some clarification on how run the two NSTimer simultaneously or keep track of all NSObjects.
Object creation in viewcontroller is
#property(nonatomic,strong)Toast *toast;
No need to keep the istance of timer in a ivar for your purpose. Use the userinfo: parameter to pass the view you mean to hide when the timer fires, like this:
[NSTimer scheduledTimerWithTimeInterval:(4.0f)
target:self
selector:#selector(xxxx)
userInfo:yourView repeats:NO];
Then in your selector retrieve the view from the userInfo and hide it. You can find a working sample here, line 37.
I have a tab bar application with three tabs. In my second tab view i have a UIProgressView. I have a NSTimer in my viewDidLoad method which calls a method which updates the progressView
progressTimer = [NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector:#selector(updateProgress) userInfo:nil repeats:YES];
All is fine so far.
I want to know how do i run the timer in the background throughout the lifecycle of the app, so that progressView is updated no matter which view the user is in. It will be great if you can show me with a code snippet.
Thanks in advance.
It's not advisable to update UI elements from background threads. Also, it's not advisable to modify UI elements of a view when the user is not in that view. You are using precious system resources.
Better way is to update the ProgressBar as soon as that view becomes active...
UPDATE: You can see here to know how to run a task in background thread. You could set up your NSTimer to start in the selector you specify there. But again, this kind of stuff could lead to some weird bugs. Better to avoid..
You could keep a property in the ProgressViewController (or even in the app delegate), say a float that tracks the progress. When you need to update the progress, another ViewController can change the value of the property.
Once the view becomes visible, update the UI (UIProgressView) from viewWillAppear:.
u can add the method in background this way
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Add code here to do background processing
// increment the progress here and keep the property of that count in appdelegate
//
dispatch_async( dispatch_get_main_queue(), ^{
// Add code here to update the UI/send notifications based on the
// results of the background processing
});
});
in diffent view's viewWillAppear u can set this way:
[loadingProgressView setProgress: appDelegate.count];
I am creating a repeating NSTimer object that calls the -Loop() method every iteration of the run loop:-
NSTimer *loopTimer = [NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:#selector(Loop) userInfo:nil repeats:YES];
Within Loop() I am invalidating the timer when it becomes necessary:-
[loopTimer invalidate];
However, after using -invalidate(), the Touch handling methods -touchesBegan(), -touchesEnded() stop responding to touch events. Does NSTimer affect the run loop to somehow?
what else you are using in loop method...
I am not sure what problem exactly you have.Anyway try this...
You can use schedular method..
[self schedule:#selector(loop) interval:TIMER_INTERVAL];
If you unscheduling with in the same function,you can use
[self unschedule:_cmd];
otherwise,
You can use [self unschedule:#selector(loop)];
This sounds like you didn't correctly retain the view or have released it once too often. A NSTimer retains its target object, and once you invalidate a timer it releases its target object. In your case, that seems to make the retain counter drop to 0 and it thus gets deallocated.
You can verify this by adding a NSLog right in front of your [loopTimer invalidate]; and another one at the start of your view's dealloc. My bet is that you will see dealloc's log message immediately after your invalidate log.
No, it doesn't affect your runloop in anyway. Infact I checked it myself, after i invalidated the timer i was able to process touches on screen.So i guess problem is something else.
I seem to have fixed the problem by putting -invalidate() inside the -dealloc() method. So when i want end the loop i call [self.view removeFromSuperView] and this calls invalidate eventually.
Putting -invalidate() inside view lifecyle seems to deallocate the view...
I have a weird problem invalidating NSTimer. As long as the user is on a particular screen, I need to constantly update it. I'm using NSTimer to accomplish it. I wrote the below piece of code in viewDidLoad method.
- (void)viewDidLoad {
self.pollServerForUpdates = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(fetchNewDataFromServer:)
userInfo:nil
repeats:YES];
}
Problem is when I try to invalidate the timer. As I want the app to stop polling the server when the user leaves the screen, I put the timer invalidation code in viewWillDisappear method.
-(void) viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:YES];
//NSLog(#"%d",[self.view retainCount]);
[self.pollServerForUpdates invalidate];
self.pollServerForUpdates = nil;
}
I use a navigation controller to go back and forth between my main view and the polling view. The app crashes if I move between my main view and polling view real fast. I enabled the NSZombie to see whats happening and this is what I get
*** -[CALayer retainCount]: message sent to deallocated instance 0x1c3be0
If I remove the timer invalidation my app works fine. But I want to stop the polling (timer) as soon as the user leaves the screen.
I believe this is happening because the timer is called a split second before the view is released, how do I avoid it? Do I need to change my design? Any help will be appreciated.
p.s: I can't use push notifications on this screen.
It may be that the bug is somewhere else, with some other class that is using your view without holding a reference to it. If you don't invalidate your timer than it will have a reference to your view forever, potentially extending its lifespan and masking memory management bugs elsewhere in your code.
Try breaking on exceptions, and see where the call to the zombie is coming from.
I am currently working on a project where I request and parse multiple html sites in a controller. To give some feedback to the user I have created a second view, which gets displayed during processing of the data. It displays a status label and a progressbar. In my controller I have several points where I update my labels text. Unfortunately this works only sometimes. I guess thats because the label gets redrawn only once in a while and I try to do it probably more often than once a second.
It does not work to force a redraw with [label setNeedsDisplay];
I also made a thread which updates the labels text with the text of a global variable and also calls setNeedsDisplay on the label every second. But the results in the same. I only see certain changes, but not all.
Everything is setup properly and the label never is nil. When I log my updateMethod everything seems allright. It just does not get displayed!
Cheers
Here is the code of my threads
- (void)startUpdateStatusThread{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(updateFrequently) withObject:nil waitUntilDone:NO];
[pool release];
}
- (void)updateFrequently{
NSLog(#"updateFrequently: %#", currentStatus);
test++;
[self.statusLabel setText:[NSString stringWithFormat:#"%# - %i", currentStatus, test]];
[self.statusLabel setNeedsDisplay];
[NSTimer scheduledTimerWithTimeInterval:0.0001 target:self selector:#selector(updateFrequently) userInfo:nil repeats:NO];
}
Am I right if I assume that you call your label's text-property as well as setNeedsDisplay from within your "Thread" that is parsing your websites?
Note that: changes to UIViews (or subclasses of UIView like your label) must be performed on the main thread.
What I recommend you to do is write a helper method that updates your label and calls setNeedDisplay, the from within your Parser-Thread call
[mainViewController performSelectorOnMainThread:#selector(yourUpdateLabelHelper:) withObject:nil waitUntilDone:NO];
that should get your job done.
You mention threads. Be aware that UIKit controls, such as UILabel, can only be updated from the main thread. Make sure you are only attempting to set the label's text from the main thread and then verify if you are still having issues.
EDIT due to question edit:
First, -setNeedsDisplay only tells the view it needs to redisplay the next time the screen is refreshed, it does not force a display at that time.
Second, the iPhone screen draws at about 60 hz. This means there is an update every 0.016666 or so seconds. You're trying to update much faster than that, so only about every 160th of your updates will actually be displayed.
Third, and probably most important, you don't seem to be making any threads, unless you call -startUpdateStatusThread from a background thread. Either way, once you hit -updateFrequently everything is back on the main thread. With the frequency you are scheduling that timer, you are probably overloading the CPU and making it so that the system does not have time to draw the label. So even though you are setting new text, you aren't giving it a chance to render. You need to reduce the frequency of that timer, and you probably need to rethink whatever it is you're trying to do.
I think that the creation of the timer should be in separate function:
Allow the repetition and also store the timer in a member variable you can close on dealloc.
[NSTimer scheduledTimerWithTimeInterval:0.0001 target:self selector:#selector(updateFrequently) userInfo:nil repeats:YES];
}
The update function should operate as a callback function.
Regards
Assayag