NSTimer questions - objective-c

So I have two problems but both are related.
I created a "animation" that moves a progress view when a button is clicked by using NSTimer. This animation works fine the first couple times the button is clicked but after that it starts speeding up the process, practically skipping from the initial start of the NSTimer loop to the end of the NSTimer loop. I was wondering if anyone has ever heard of this issue and/or knows a solution?
I created the same thing in question 1 but in a UItableViewCell and the NSTimer loop is activated when the edit button is pressed. The edit button activates a function that has this [self.tableView2 setEditing:NO animated:YES]; and the NSTimer scheduledTimerWithInterval ... (The progress view animation). The issue here is that it the setEdit animation no longer animates it just pops into place. Once again, does anyone know of a solution for this?
Here is the code for question 2 (Both the code for the questions are very similar so if you can spot the problem here then it is likely I can use that solution to fix them both):
-(void)editTable{
[self.tableView2 setEditing:YES animated:YES];
iCount = 8;
iCount2 = 257;
forwardProgress = YES;
animationFlag = YES;
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.001f target:self selector:#selector(increaseAmount) userInfo:nil repeats:YES];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(cancelEdit)];
}
-(void)cancelEdit{
[self.tableView2 setEditing:NO animated:YES];
forwardProgress = NO;
animationFlag = YES;
iCount = 38;
iCount2 = 227;
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.001f target:self selector:#selector(increaseAmount) userInfo:nil repeats:YES];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:#selector(editTable)];
}
-(void)increaseAmount{
float max = 38.0f;
float max2 = 257.0f;
float min2 = 227.0f;
float min = 8.0f;
if (animationFlag == YES) {
if (forwardProgress == YES) {
if (iCount <= max) {
iCount++;
// NSLog(#"iCount = %i", iCount);
}
if (iCount2 >= min2) {
iCount2--;
// NSLog(#"iCount2 = %i", iCount2);
}
if (iCount <= max) {
[tableView2 reloadData];
}
if (iCount2 >= min2) {
[tableView2 reloadData];
}
if (iCount == max) {
animationFlag = NO;
}
newFrame = CGRectMake(iCount, 33.0f, iCount2, 11.0f);
[tableView2 reloadData];
}else{
if (iCount >= min) {
iCount--;
NSLog(#"iCount = %i", iCount);
}
if (iCount2 <= max2) {
iCount2++;
NSLog(#"iCount2 = %i", iCount2);
}
newFrame = CGRectMake(iCount, 33.0f, iCount2, 11.0f);
if (iCount >= min) {
[tableView2 reloadData];
}
if (iCount2 <= max2) {
[tableView2 reloadData];
}
if (iCount == min) {
animationFlag = NO;
}
}
}
}

Any timer invalidates itself when it has finished unless it has repeats:YES. If that's the case, you need to invalidate it yourself at some point, ideally when all your conditions are met. In your case I think you'd want it when you're setting animationFlag == 0 (I presume that's when you want the timer to stop firing). Use something like this:
if ([myTimer isValid]) {
[myTimer invalidate];
myTimer = nil;
You also need to place that code in editTable at the beginning. That way, if a timer was running and someone started it again, it would first reset, and then begin again.Otherwise you have multiple timers all firing increaseAmount. You also need it anytime the user cancels the timer.

You probably have to invalidate the timer from your first animation. I suspect that you have two timers running and therefore the speed of the animation is doubled.
This is probably because of the reloadData call in your timer callback. A tableview cannot reload and animate at the same time.

I believe rgeorge is correct;
You don't get rid of the "myTimer" when you stop your animation. So even though you may not see the animation any more, increaseAmount is still being called over and over. Change the method to something like this:
-(void)increaseAmount:(NSTimer*)Timer;
Then, when you're done with increase amount, make sure to turn off the timer from within increaseAmount.

Related

Use for loop with UIAnimation in Objective C

I want to show number in a UILabel from an unknown number to unknown number. Suppose from 40 to 699. I want to show this one by one and then break the loop sequence when required number is arrived.
E.g {40,41,42...667,668,669}
I want to remove the previous number from the label and add new number to the label object.
I am confuse to use UIAnimation in for() Loop.
Does anyone have any idea how to do this?
Using timer to animation with increase counter.
int loopCount;
NSTimer *myTimer;
// Method that calls your timer.
- (void)doStuff {
loopCount++;.
self.yourLabel.alpha = 0;
[UIView animateWithDuration:0.65 delay:0 options:UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse animations:^{
self.yourLabel.alpha = 1;
} completion:nil];
if (loopCount >= 669) {
[myTimer invalidate];
myTimer = nil;
self.yourLabel.alpha = 0;
}
}
// Method that kicks it all off
- (IBAction)startDoingStuff {
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.75
target:self
selector:#selector(doStuff)
userInfo:nil
repeats:YES];
}

Objective-C, Displaying an NSTimer activated by a UIButton

I'm working on a project for school that includes a set of buttons that create NStimers used as a countdown timer. Ideally, the time would display as the title of the UIButton that started the timer.
So far I have only been able make this work if when setting the display of the time I refer to a specific instance of a UIButton rather than a somehow passing a UIButton argument.
I also have declared "time" NSString that holds the current displayed value calculated by the NSTimer. This is not how the final implementation should be, and I only have it set up like this just to have it partially working.
Code for one of the buttons:
b1 = [[UIButton alloc]
initWithFrame:CGRectMake(neutral.frame.size.width * 0.56, neutral.frame.size.height * 0.18, neutral.frame.size.height * 0.64, neutral.frame.size.height * 0.64)];
[b1.titleLabel setFont:[UIFont fontWithName:#"Helvetica-Bold" size:35.0]];
[b1 setBackgroundImage: forState:UIControlStateNormal];
Method called from button press action:
-(void)buttonClicked:(id)sender
{
//do stuff
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timerFired) userInfo:[NSNumber numberWithInt:t] repeats:YES]; }
NSTimer timer fired method:
-(void)timerFired
{
if((curMin>0 || curSec>=0) && curMin>=0)
{
if(curSec==0)
{
curMin-=1;
curSec=59;
}
else if(curSec>0)
{
curSec-=1;
}
if(curMin>-1)
{
time = [NSString stringWithFormat: #"%d%#%02d",curMin,#":",curSec];
[b1 setTitle:time forState:UIControlStateNormal];
}
}
else
{
[b1 setTitle: nil forState:UIControlStateNormal];
[timer invalidate];
}
}
I hope that was clear enough as to what I am trying to accomplish. I am somewhat surprised that I haven't been able to find anything to walk me through this. I am flexible to changing how exactly this works. All that really matters is that each button can start a unique countdown timer.
I'm sure that this code is sloppy, but Objective C is relatively new to me so any help at all would be appreciated.
Don't store time as minutes/seconds, just use seconds, as all that greater than 60 logic is really tedious isn't it.
I've chosen to call the instance variable _secondsLeft so I will know what it means when I look at the code in 6 months time:
-(void)timerFired
{
NSString *formatted = #"";
if (secondsLeft > 0)
{
secondsLeft--;
formatted = [NSString stringWithFormat:#"%d:%02d", (secondsLeft / 60), (secondsLeft % 60)];
} else {
[timer invalidate];
}
[b1 setTitle:formatted forState:UIControlStateNormal];
}

Objective c : How to set time and progress of uiprogressview dynamically?

Say I have a UIProgressBar with size (394,129,328,9) and I increased the height with
CGAffineTransform transform = CGAffineTransformMakeScale(1.0f, 3.0f);
PgView.transform = transform;
What I need to implement is : If I have 5 minute time,the progressView should be filled with a certain amount and totally filled at the end of 5 minute. Same for 1 minute and 30 minutes too.
I am implementing like this :
-(void)viewWillAppear:(BOOL)animated
{
CGAffineTransform transform = CGAffineTransformMakeScale(1.0f, 3.0f);
PgView.transform = transform;
recievedData = 0.01;
xpectedTotalSize = 1.0;
PgView.progress = 0.0;
[self performSelectorOnMainThread:#selector(makeMyProgressBarMoving) withObject:nil waitUntilDone:NO];
}
- (void)makeMyProgressBarMoving
{
float actual = [PgView progress];
if (actual < 1)
{
PgView.progress = actual + ((float)recievedData/(float)xpectedTotalSize);
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(makeMyProgressBarMoving) userInfo:nil repeats:NO];
}
else{
}
}
And it works. But I want to implement progress and time interval dynamically? Any idea??
Thanks in advance.
It's difficult to understand your intention. As I suppose, you want to update the progress bar only if new values are available instead of updating with a fixed timer interval.
This would result in the simple answer: update your progress bar whenever you change the value of 'recievedData'. So, the method 'makeMyProgressBarMoving' doesn't need a timer. Also, you don't need to remember the 'actual' value. You can directly assign the 'receivedData/xpectedTotalSize' to the progress bar.
- (void) updateProgressBar
{
PgView.progress = ((float)receivedData/(float)xpectedTotalSize);
}
- (void) updateReceivedData
{
if (receivedData < 20)
receivedData += 1.0;
else
receivedData += 10.0;
[self updateProgressBar];
}
-(void)viewWillAppear:(BOOL)animated
{
receivedData = 0.0;
xpectedTotalSize = 100.0;
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(updateReceivedData) userInfo:nil repeats:YES];
}
Now, a timer is still used for demonstration to call 'updateReceivedData' which just increments 'receivedData' and calls 'updateProgressBar'. As you see, 'updateReceivedData' does not increment linear, again just for demonstration. If 'updateReceivedData' would be called on demand instead by the timer then also the progress bar would be updated on demand. Now it's just an issue of how and when you want to update your receivedData. It's not an issue regarding progress bars.

how to stop repeat timer?

I am a beginner when it comes to iOS App development. I want to move a label from left to right until it reaches half the screen width - i.e. the label should move by 240px (the label moves left to right like a marquee).
I have used NSTimer and I want to stop the timer when the label reaches half the view's width.
I have used the following code but it moves the label out of the view:
- (void)viewDidLoad {
[super viewDidLoad];
timer = [[NSTimer scheduledTimerWithTimeInterval:0.09 target:self selector:#selector(time:) userInfo:nil repeats:YES] retain];
}
- (void)time:(NSTimer *)theTimer {
label.center = CGPointMake(label.center.x+3.5, label.center.y);
NSLog(#"point:%#", label);
if (label.center.x < - (label.bounds.size.width/2)) {
label.center = CGPointMake(320+(label.bounds.size.width/2), label.center.y);
}
}
How can I solve this, please?
If you want to stop the repeating timer, you can use
if (/*You label is in position*/)
[myTimer invalidate];
But that's not the normal way to do animation in iOS, try this instead:
CGRect endFrame = /*The frame of your label in end position*/
[UIView animateWithDuration:0.5 animations:^{ myLabel.frame = endFrame;}];
To stop a timer, do [timer invalidate].
You can't "pause" a timer, so once you do this you'll need to call another timer.
correct way to invalidate your timer is
[myTimer invalidate];
myTimer = nil;

Making fading effect with NSView

I have a subview which I want to show up to make a dissolve fade effect. For that purpose, I'm trying to:
Show it
Fade it
For that purpose, I set a "showFade" method on my superview (Fader is the subview defined previously. someImage is already defined).
- (void)showFade {
[Fader setHidden:NO];
[Fader setImage: someImage];
[[Fader animator] setHidden:YES];
}
The problem is: it works for the first time, and the subview fades, but it never shows up again. What am I doing wrong?
EDIT: As requested, here is a more complete sample of the code
- (void)awakeFromNib {
Fader = [[NSImageView alloc] initWithFrame: [self bounds]];
Images[0] = #"Tower";
Images[1] = #"Desert";
Images[2] = #"Fall";
image = 0;
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(showFader:) userInfo:nil repeats:YES];
[self addSubview: Fader];
[self nextImage];
[super awakeFromNib];
}
- (void)showFader: (NSTimer*)timer {
[Fader setImage: [self image]];
[Fader setHidden:NO];
[[Fader animator] setHidden:YES];
[self nextImage];
}
- (void)nextImage {
if (image == 2) {
image = 0;
}
else {
image++;
}
[self setImage: [NSImage imageNamed: Images[image]]];
}
So, basically, I have a repeating timer making the parent NSImageView to loop between an array of images and making the "Fader" to show up with the previous image and fade out. The problem is that it only shows up once.
I had the same problem a long time ago (yet I never solved it), but I think this could have something to do with the fact that you ask your view to be showed and dissolved in the same event loop.
I guess you should let the event loop make an iteration (so that your view is actually displayed) before asking to dissolve the view.
I guess you could do it by setting a (short) timer that would fire a couple of millisecs after the view is displayed (by setHidden:NO).
I also guess there are easier ways to solve the problem (and I would be glad to hear them).
What is more, you could also try animator setAlphaValue:0.0 to have a smooth transition.
I am not definitely not sure I have a correct answer, that's only a suggestion...
hidden is a BOOL property, you can't animate BOOLs cause there are only two discrete values: NO and YES. Use the opacity instead, you can animate opacity from 1.0 (fully visible) to 0.0 (invisible) to fade out. And hidden has to be set to NO for this to work, otherwise it will be hidden all the time.
This method works perfectly for me to make an image start out blurry and smoothly come into focus.
let parentView = UIView()
let imageView = UIImageView(image: UIImage(named: "ImageToFocus"))
parentView.addSubview(imageView)
let blurEffect = UIBlurEffect(style: .Light)
let blurView = UIVisualEffectView(effect: blurEffect)
blurView.frame = imageView.frame
blurView.alpha = 1.0
self.blurTimer = NSTimer.scheduledTimerWithTimeInterval(0.05, target: self, selector: Selector("animateBlur:"), userInfo: blurView, repeats: true)
func animateBlur(timer:NSTimer)
{
let blurView = timer.userInfo as UIVisualEffectView
if blurView.alpha > 0.0 {
blurView.alpha -= 0.05
} else {
blurTimer = nil
}
}