I am creating an application where I have several buttons on the screen and I want to make them move throughout the screen. User can tap on that buttons and the buttons on which user taps gets removed from the screen.Hence I have to stop the timer which moves that button.
But my main concern is How to make all the bubbles move i.e. How to manage each of them?
I have made the use of NSTimer.
NSTimer* timer = [NSTimer timerWithTimeInterval:1 target:self selector:#selector(moveTheButton) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
But this can be helpful in case if I have only one button. If there are more buttons its quite hard to manage the timers.
Also the number of buttons are dynamic.
Please suggest some alternate.
I have gone through few threads but didnt got the expected answer.
Thanks in advance for help.....
I would suggest using CAAnimations to move your buttons. This would allow you to animate your buttons smoothly without the use of NSTimers and would also be ideal for creating your button animations dynamically.
CAAnimations are created then added to a views layer. You can animate the rotation, position and size of a view using CAAnimations. It is also easy to add and remove animations from a views layer, making it ideal for dynamic content without the headache of multiple NSTimers.
How should a UIButton move? Do you mean something like randomly moving each button like in a Brownian motion? : D
Well, it really does not matter I guess, but let's consider that you have a generalized function that determines the behavior of each button given its current position, in this case you can use an NSArray to keep track of all your buttons positions at each time and just call this function over the single NSArray (then cycling through its elements) as the selector of the NSTimer.
If you do not have a single generalized function, but each button has its own, you could subclass UIButton for each kind of motion you have to take care and implement a method for each class that implements the correct motion. Then you can have a function called in NSTimer that cycles through an array of references of the buttons and calls the implemented motion method for each instance (via polymorphism)
Related
I have a simple UIView that draws itself using drawRect:. In order for the view to animate, the drawRect method needs to be called every say 0.05 seconds, so I use a repeating timer:
timer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self
selector:#selector(setNeedsDisplay) userInfo:nil repeats:YES];
I don't know too much about run loops, and threads, and all that system stuff, so I want to know if this is the correct way to run an animation? This timer repeats itself endlessly. Is this something I should be worried about? Will this block the main thread? What can I do to minimize overall impact on performance?
The approach is not bad, but there are other ways to do it.
The timer's target method must take the timer as an argument, so instead of setting it for setNeedsDisplay directly, you should set up a method like this:
- (void)animationTimerDidFire:(NSTimer *)timer
{
[myView setNeedsDisplay];
}
If your view will always be visible, then you can just set-and-forget the timer. On the other hand, if it may go away because you switch to a different view, you will need to invalidate and recreate the timer as needed.
The main thread of your app uses a run loop and calls out to various methods in response to events, like user taps, system notifications (e.g., memory warning), and I/O arriving. If anything the run loop calls takes a long time to return, it will hold up everything in the queue. When you set up a timer, it is added to a list and that the run loop checks it each time through; if one of the timers is ready, it calls your method.
The end result is that timers are not exact: they might not fire as often as you like, might be called late, etc. Again, if your app is pretty simple, the main run loop won't be very busy and so a timer will probably be good enough. Just make sure your animation is based on actual time elapsed between calls, rather than assuming each call happens exactly 0.05 seconds apart.
Alternatives
If your animation simply involves flipping through some static images, UIImageView has some support for this.
If creating each frame of animation takes a noticeable amount of time (and you don't want to block the main thread), you could use a background queue to draw into an image (see CGBitmapContextCreate and CGBitmapContextCreateImage), then signal the main thread when a new image is ready to display. Anything that touches a view MUST happen on the main thread, but you can do the drawing on the background.
You also might want to read up on CALayer in the QuartzCore framework. This is what the UIView objects actually manipulate to draw on the screen. You may find that, instead of drawing, you can get the effects you want by manipulating some CALayer objects and letting Core Animation do the heavy lifting (e.g., you change the color from red to blue, Core Animation takes care of fading from one to the other).
Well, if you are using an overridden or custom method, you should use recursion and a completion block for calling the animation. I find it works much better than a timer, since timers aren't always exact and can cause some animation issues if you have the animations timed for cycling.
EDIT: I don't know much about using drawRect: and calling [self setNeedsDisplay] to update it, so I can't help you in that regard. Sorry.
This only happens once in a while. When I step from a breakpoint in the method called by the menu item when pressed I end up at the end of the method and when I step out I eventually get to ccTouchEnded and then the bad access occurs. Nothing shows up in the debug output window but I get a green arrow pointing to the main method with the error message.
Any ideas why this might occur?
Thanks.
So in case anyone has the same problem, I figured out what was happening. I had a CCMenu containing several children. When a child was tapped I did what I wanted with it and then removed it from the CCMenu via removeChild:cleanup: in the method I passed as the selector for the CCMenuItem. The problem was that Cocos2d deactivates the CCMenuItem while the selector method is executed and then reactivates it when the method is finished. So in the method I was basically destroying the CCMenuItem by removing it from the CCMenu and then at the end of the method Cocos2d tried to reactivate it but it was no longer in memory.
I don't see much of a way around this, so maybe it is not possible to remove a CCMenuItem from a CCMenu in its selector method.
The way I worked around it was to simply call setVisible:NO and setIsEnabled:NO on the menuitem. However, I can imagine cases in which this would not be the best way to do it. Maybe in these cases you could mess with the z position or something to get the menuitem out of the way.
Anyway, I hope this helps someone else, I know I've been stuck on this a while. :)
A better solution, in my humble opinion, is to unwind the scene destruction call from the stack. Using something like NSTimer+BlockKit makes it really clean. Here's an excerpt from my code:
- (void)menuAction
{
// we use a timer here to delay the execution of the action because it
// destroys the current scene and we're mid a call on CCMenu's ccTouchEnded
// that isn't expecting a scene tear down
// http://stackoverflow.com/questions/11165822/exc-bad-access-occurring-when-cocos2d-calls-cctouchended-in-ccmenu
[NSTimer scheduledTimerWithTimeInterval:0 block:^(NSTimer* timer)
{
[[CCDirector sharedDirector] popSceneWithTransition:
[CCTransitionSlideInL class] duration:kTransDur];
}
repeats:NO];
}
I'm wondering if iOS allows one to do the following:
I have a puzzle game and I've been working on saving data when the user returns to the home screen. To do this, using NSNotificationCenter, I had one of my game classes to observe [UIApplication sharedApplication]'s ApplicationWillResignActive method so when that happens I save my game state. But if the user decides to exit while animations are going on, the game will save a midState where the model values are still changing and that will often cause crashes. My question is if it is possible to somehow delay the saving process (even though it is on the background) until the animations are complete (or some variable equals 1)?
My first idea is to create scheduled event with NSTimer to try to save until everything is set. Any ideas would be appreciated. Thank you
You can use performSelector:withObject:afterDelay:
// Call doSomething after a 1 second delay
[self performSelector:#selector(doSomething) withObject:nil afterDelay:1.0f];
Rather than trying to delay the saving, especially in ApplicationWillResignActive, you should look into how to stop the animation and record the expected final values of the animation. Depending on the method of animation (UIView static methods, block based, 3rd party) there is usually a way to stop them, and since you define what the animation does you should already know the final state.
In the case of block based animations:
How to cancel UIViews block-based animation?
I have a spinning circle UI element that is updated by an NSTimer. I also have a UIScrollView that would 'block' the spinning circle while being scrolled. That was expected, because the timer and the scroll view where both in the same thread.
So I put the timer in a separate thread which basically works great! I can see it working because NSLogs keep coming even while scrolling.
But my problem is that my spinning circle is still stopping on scrolling! I suspect redrawing is halted until the next iteration of the main thread's run loop (?). So even if its angle is changed all the time, it might not be redrawn...
Any ideas what I can do? Thanks!
While scrolling, the main thread's run loop is in UITrackingRunLoopMode. So what you need to do is schedule your timer in that mode (possibly in addition to the default mode). I believe NSRunLoopCommonModes includes both the default and event tracking modes, so you can just do this:
NSTimer *timer = [NSTimer timerWithTimeInterval:0.42 target:foo selector:#selector(doSomething) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
By the way, you should do this all on the main thread. No need to spawn a background thread (unless each timer invocation is going to do some lengthy processing).
UIScrollView doesn't just block NSTimers, it blocks the entire main thread. And UIKit objects should be accessed on the main thread only (and, often, are limited in unpredictable ways if you try to dodge round that restriction), which is probably why — even if you have a clock coming in from an external thread — you're unable to post updates.
It's a bit of a dodge, but is there any way your animation can be achieved with CoreAnimation? Things tied to that continue working irrespective of what's happening in the main thread.
You are correct that UIScrollView does not update its content view during scrolling.
Probably your best bet -- and this is something of a hack -- is to display your animating view separately (perhaps within a frame-view, to get the free clipping) and use the scroll-view delegate callbacks to find out the current position of the scroll and position your animation accordingly.
NOTE: this suggestion is a hack! It's not best practices and it's certainly counter to simple, maintainable code without side effects. However, it's the only way I can think to get the display you want.
Another tack: re-think the importance of displaying animated contents of a scrolling view while it is scrolling.
I have this big problem that i dont know how to fix. I have a UIView that i want to draw a scrolling background on. I am using NSTimer to update 30 frames per second but it seems to redraw one frame every 8 seconds. I am calling [self setNeedsDisplay] but it has no effect. I cant figure out why this is happening, does anyone have any tips?
Thanks for your time.
I have an NSTimer in my applicationDidFinishLaunching method
timer = [NSTimer scheduledTimerWithTimeInterval:0.0
target:self
selector:#selector(gameLoop:)
userInfo:nil
repeats:YES];
It's messaging this method:
-(void) gameLoop: (id) sender
{
[myView updateAll];
[myView setNeedsDisplay];
}
myView is a UIView. UpdateAll updates my code for drawing. It works fine, the problem seems to be the drawRect method is being executed so infrequently. I need to know how to make it execute 30 frames per second. I keep seeing people say that all you have to do is what i have above but setNeedsDisplay still only gets called every 8 seconds.
Thank you for your help...
Redrawing the view is an expensive operation, which will decrease performance considerably. If you can instead, just have the background as an image view move across the background using [UIView beginAnimation... or using CAAnimations.
Don't use NSTimer.
From the NS Timer reference
Because of the various input sources a typical run loop manages, the effective resolution of the time interval for a timer is limited to on the order of 50-100 milliseconds.
I'm not an expert on the iPhone, but you probably need to look into animation on the platform to achieve the update interval you want.
It turns out that my code was just incredibly inefficient and that was causing the slow down not the timer. If you have a similar problem, make sure to test your drawRect with some simple drawing (such as drawing your Frames Per Second to the screen) before you assume it is not being executed. Thanks for viewing.