Hi and thanks in advance for any help and contribution :)
I am currently working on a personal project(some sort of a mmorpg) AND I'am stuck at the walking animation.
Until now I have made the map and implemented the A* pathfinder that returns a path array in C style.
What I am trying to do is to change the background image of an UIImageFrame and de/increment its x and y position depending on the values stored in an array.
The ideea was to change the background image and de/increase X or Y by 2px using an NSTimer, every 0,1s.
The NStimer will call the animation function 32 times before it is invalidated.
And here is the code for the 4 possible directions( N/S/W/E):
In the .h file:
#import <UIKit/UIKit.h>
int xMyCharacterFrame;
int yMyCharacterFrame;
#interface ViewController : UIViewController
{
int counterInMilisecondsForAnimation;
UIImageView *myCharacterFrame ;
}
#property (strong, nonatomic) NSTimer *timerForSouthMoving;
#property (strong, nonatomic) NSTimer *timerForNorthMoving;
#property (strong, nonatomic) NSTimer *timerForEstMoving;
#property (strong, nonatomic) NSTimer *timerForWestMoving;
-(void)southAnimation;
-(void)northAnimation;
-(void)estAnimation;
-(void)westAnimation;
#end
In the .m file:
- (void)viewDidLoad {
[super viewDidLoad];
myCharacterFrame = [[UIImageView alloc]init];
myCharacterFrame.frame = CGRectMake(xMyCharacterFrame, yMyCharacterFrame, 100, 100);
myCharacterFrame.image = [UIImage imageNamed:#"scientist_s0.png"];
}
When the game is started the xMyCharacterFrame are laoded form NSUserDefaults. ANd the recalculated depending on the user tap. The game is a 2d map based, with tiles of 64*64 px.
-(void)findPathForStart:(int)start andFinish:(int)stop{
//after the path is find using the self implemented A* pathfinder, the walking animation should use
the resulted path find array, that is two dimensional, first position Y and on the second the X.
On the fist 2d array of the path array I store the finish position, so in order to animate I need to read it in reverse.
int xWayOfMoving;
int yWayOfMoving;
for (int possitionInThePathArray = sizeOfPathArray - 1; possitionInThePathArray >= 0; possitionInThePathArray--) {
xWayOfMoving= pathArray[possitionInThePathArray-1][1] - pathArray[possitionInThePathArray][1];
yWayOfMoving= pathArray[possitionInThePathArray-1][0] - pathArray[possitionInThePathArray][0];
if (xWayOfMoving== 0 && yWayOfMoving== 1) {
//animate north
[timerForSouthMoving invalidate];
timerForSouthMoving = nil;
counterInMilisecondsForAnimation= 0;
timerPentruAnimatieDeplasare = [NSTimer scheduledTimerWithTimeInterval: 0.1
target: self
selector:#selector(northAnimation)
userInfo: nil
repeats:YES];
}else if (xWayOfMoving== 0 && yWayOfMoving== -1){
//animate south
[timerForNorthMoving invalidate];
timerForNorthMoving= nil;
counterInMilisecondsForAnimation= 0;
timerForNorthMoving= [NSTimer scheduledTimerWithTimeInterval: 0.1
target: self
selector:#selector(northAnimation)
userInfo: nil
repeats:YES];
}else if (xWayOfMoving== 1 && yWayOfMoving== 0){
// animate west
[timerForWestMovinginvalidate];
timerForWestMoving= nil;
counterInMilisecondsForAnimation= 0;
timerForWestMoving = [NSTimer scheduledTimerWithTimeInterval: 0.1
target: self
selector:#selector(northAnimation)
userInfo: nil
repeats:YES];
}else if (xWayOfMoving== -1 && yWayOfMoving == 0){
//animate est
[timerForEstMoving invalidate];
timerForEstMoving = nil;
counterInMilisecondsForAnimation= 0;
timerForEstMoving = [NSTimer scheduledTimerWithTimeInterval: 0.1
target: self
selector:#selector(northAnimation)
userInfo: nil
repeats:YES];
}
}
}
One of the 4 functions for the walking animation:
-(void)northAnimation{
if (counterInMilisecondsForAnimation< 33) {
if (counterCharacterImageSouth == 11) {
counterCharacterImageSouth = 0;}
myCharacterFrame.frame = CGRectMake(xMyCharacterFrame, yMyCharacterFrame, 100, 100);
myCharacterFrame.image = [UIImage imageNamed:[NSString stringWithFormat:#"scientist_s%d.png", counterCharacterImageSouth]];
yMyCharacterFrame = yMyCharacterFrame - 2;
counterCharacterImageSouth++;
counterInMilisecondsForAnimation++;
}
else{
[timerForSouthMoving invalidate];
}
}
Related
I am working on an app and I want to animate the numbers in my app. I know I need to use an NSTimer. Just not sure how. For example I want the app to count up from 0.00 to 176.20 (self.total.text).
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(update) userInfo:nil repeats:YES];
- (void)update{
float currentTime = [self.total.text floatValue];
float newTime = currentTime + 0.1;
self.total.text = [NSString stringWithFormat:#"%f", newTime];
}
You need to decide at what increment you want to count. You want to stop at 176.20 so it seems like an increment of 0.1 seconds would be what you want. You need a variable to store the current position.
Obj-c///
const float limit = 176.2f
#property (nonatomic) float seconds;
#property (nonatomic, strong) NSTimer *updateTimer;
// Initialize
self.seconds = 0.0f;
self.updateTimer = [NStimer scheduledTimerWithTimeInterval:0.1f target:self selector:#selector(timerFired) userInfo:nil repeats:true];
Swift///
var seconds = 0.0
let limit = 176.2
let timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("timerFired"), userInfo: nil, repeats: true)
Then you need to create a function which is used to update the label every time the timer fires and a function which schedules the timer.
Obj-c///
- (void)timerFired {
self.seconds += 0.1f;
self.label.text = [NSString stringWithFormat:#"%f", self.seconds];
if (self.seconds >= limit) {
[self.updateTimer invalidate];
}
}
Swift///
func timerFired() {
seconds += 0.1 //Increment the seconds
label.text = "\(seconds)" //Set the label
if (seconds >= limit) {
timer.invalidate()
}
}
I have a homepage that showing the value of incomes and expenses. Right now the values just appear on the the screen, however is was wondering whether i can make those number start from 0 and then have an animation to increment that value, like a stopwatch. Is it also possible to make the final number after the increment has finished to animate with a pulse". This is how i am making the label pulsate now but the label becomes bigger from the left had side and not from the center.
[self.view addSubview:_lblTotal];
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
scaleAnimation.duration = 0.4;
scaleAnimation.repeatCount = HUGE_VAL;
scaleAnimation.autoreverses = YES;
scaleAnimation.fromValue = [NSNumber numberWithFloat:0.8];
scaleAnimation.toValue = [NSNumber numberWithFloat:1.0];
[_lblTotal.layer addAnimation:scaleAnimation forKey:#"scale"];
The following open source UILabel subclass does the scrolling numbers bit you are looking for.
https://github.com/dataxpress/UICountingLabel
Example of the type of usage is:
myLabel.method = UILabelCountingMethodLinear;
[myLabel countFrom:50 to:100 withDuration:5.0f];
I would look to see how that is done and then extend it to add the pulse at the end once the animation for the counting has completed.
yes this is possible.
add a variable with the number to increment
#property (nonatomic) double currentValue;
#property (nonatomic, retain) NSTimer *timer;
initialize it in viewDidLoad
_currentValue = 0.00;
write a function updateNumber
-(void)updateNumber {
_currentValue = _currentValue + 0.01;
[_label setText:[NSString stringWithFormat:#"%.2f", _currentValue]];
if(_currentValue >= 100.0) { //example to stop incrementation on 100
_timer = nil; // stop the timer
// do your animation
}
}
in your viewDidAppear() or whenever you want to start the counting like this
if(_timer == nil) {
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 //seconds
target:self
selector:#selector(updateNumber)
userInfo:nil
repeats:true];
}
You might want to separate the incrementing number and the pulse animation into two different questions.
On the question of the pulse, the code is correct but the reason it scales off-center is probably due to your view's frame which is bigger than its content, which appears to be right-aligned rather than centered.
If you make _lblTotal's frame fit the content, by calling [_lblTotal sizeToFit]; your pulse animation should work as expected. But then this assumes that you do the right-alignment yourself.
Here is an example code , which will surely help you but you have to customize the timing as it certainly suit my need:-
-(void)viewDidLoad
{
currentValueAmount=0; //provide initial value for your incremented label
targetFloatAmount=1300;//provide the target value up-to which the value will be incremented.
self.lblAmount.text=[NSString stringWithFormat:#"%.2f",currentValueAmount];
[self performSelector:#selector(animateIncrementLabel) withObject:nil afterDelay:0.5f]; // give some delay as per your choice as to load the whole UI first and then calling the method to increment.
}
-(void)animateIncrementLabel
{
self.lblAmount.transform=CGAffineTransformMakeScale(0.000f, 0.000f);
timer = [NSTimer scheduledTimerWithTimeInterval: 0.001 //calling timer , if the label value is very small then increase the no of Zero's for example 0.00001
target: self
selector: #selector(handleTimer:)
userInfo: nil
repeats: YES];
//animating the incremented text with some transform and bounce
[UIView animateWithDuration:2.1 delay:0.2f usingSpringWithDamping:0.4 initialSpringVelocity:0.3 options:0 animations:^{
self.lblAmount.transform=CGAffineTransformMakeScale(1.3, 1.3);
}];
}
- (void) handleTimer: (NSTimer *) timer
{
if(currentValueAmount>targetFloatAmount)
{
self.lblAmount.text=[NSString stringWithFormat:#"%.2f",targetFloatAmount];
[timer invalidate];
timer=nil;
[UIView animateWithDuration:0.5 delay:0.2f usingSpringWithDamping:0.4 initialSpringVelocity:0.3 options:0 animations:^{
self.lblAmount.transform=CGAffineTransformMakeScale(1.0, 1.0);
}];
}
else
{
currentValueAmount=currentValueAmount+0.0008;
self.lblAmount.text=[NSString stringWithFormat:#"%.2f",currentValueAmount];
}
}
I want to use NSTimer as a Countdown. The counter works fine;-)!
But I I switch to another viewController the timer doesn't runs in "foreground" - so the label isn't updated.... after coming back to this view.
My NSLOG shows that the timer is still runnig, like I want ;-)
So in whicht way I have to work, that my timer update my label?
Here is the code:
- (IBAction)startClock:(id)sender {
globalTimer = 20;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(updateCircularProgressBar)
userInfo:nil
repeats:YES];
}
- (void)updateCircularProgressBar
{
// Values to be passed on to Circular Progress Bar
if (globalTimer > 0 && globalTimer <= 1200) {
globalTimer--;
minutesLeft = globalTimer / 60;
secondsLeft = globalTimer % 60;
[self drawCircularProgressBarWithMinutesLeft:minutesLeft secondsLeft:secondsLeft];
NSLog(#"Time left: %02d:%02d", minutesLeft, secondsLeft);
}
else {
[self drawCircularProgressBarWithMinutesLeft:0 secondsLeft:0];
[timer invalidate];
}
}
I tested to run the "updateCircularProgressBar" in my viewDidLoad but this shows NO result...
THX for Help
UPDATE more with more Code:
/*-------------------- TIMER --------------------*/
// Draws the progress circles on top of the already painted background
- (void)drawCircularProgressBarWithMinutesLeft:(NSInteger)minutes secondsLeft:(NSInteger)seconds
{
// Removing unused view to prevent them from stacking up
for (id subView in [self.view subviews]) {
if ([subView isKindOfClass:[CircularProgressTimer class]]) {
[subView removeFromSuperview];
}
}
// Init our view and set current circular progress bar value
CGRect progressBarFrame = CGRectMake(0, 0, 180, 180);
progressTimerView = [[CircularProgressTimer alloc] initWithFrame:progressBarFrame];
[progressTimerView setCenter:CGPointMake(160, 210)];
[progressTimerView setPercent:seconds];
if (minutes == 0 && seconds <= 0) {
[progressTimerView setInstanceColor:[UIColor magentaColor]];
}
// Here, setting the minutes left before adding it to the parent view
[progressTimerView setMinutesLeft:minutesLeft];
[progressTimerView setSecondsLeft:secondsLeft];
[self.view addSubview:progressTimerView];
progressTimerView = nil;
}
The label is in another file (CircularProgressTimer) which controls the progessbar...
I'm trying to rotate an UIView. This UIView represents the seconds pointer of a clock.
I need to update it every second. Like this:
- (void)startUpdates {
_updateTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(animatePointer) userInfo:nil repeats:YES];
}
Stop it when I need it to, like this:
- (void)stopUpdates {
[_updateTimer invalidate];
_updateTimer = nil;
}
And animate every second, from one second, to the next, like this:
- (void)animatePointer {
NSDate *now = [NSDate date];
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSSecondCalendarUnit fromDate:now];
float angleForSeconds = (float)[components second] / 60.0;
[UIView animateWithDuration:1.0 animations:^(void){
_pointerGlow.layer.transform = CATransform3DMakeRotation((M_PI * 2) * angleForSeconds, 0, 0, 1);
}];
}
This does work, but it's not smooth. It stops for a fraction of a second, every second, like a tipical wall clock would. Nice, but not what I'm going for.
Is there any way I could make this animation silky smooth?
You could try it with a CADisplayLink which is tied to the display refresh rate of 60/second. The code looks like this (note that I didn't add any logic to stop the display link, but I put in the method that you would call if you wanted to):
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface ViewController ()
#property (nonatomic, strong) CADisplayLink *displayLink;
#property (nonatomic) CFTimeInterval firstTimestamp;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self startDisplayLink];
}
- (void)startDisplayLink {
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(handleDisplayLink:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)stopDisplayLink {
[self.displayLink invalidate];
self.displayLink = nil;
}
- (void)handleDisplayLink:(CADisplayLink *)displayLink {
if (!self.firstTimestamp) self.firstTimestamp = displayLink.timestamp;
NSTimeInterval elapsed = (displayLink.timestamp - self.firstTimestamp);
self.arrow.layer.transform = CATransform3DMakeRotation((M_PI * 2) * elapsed/60, 0, 0, 1);
}
I use NSTimer to do the auto slide and my code is like this:
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval: 5
target: self
selector: #selector(handleTimer)
userInfo: nil
repeats: YES];
- (void)handleTimer
{
int page = _bannerScrollView.contentOffset.x / 296;
if ( page + 1 < [array count] )
{
page++;
_pageControl.currentPage = page++;
}
else
{
page = 0;
_pageControl.currentPage = page;
}
[self changePage];
}
- (void)changePage
{
int page = _pageControl.currentPage;
[_bannerScrollView setContentOffset:CGPointMake(296 * page, 0)];
}
And I need some animation stuff, because it is cool.
How can I do this? Thank you very much.
You can set frames according to ur needs and it will animate to next view with page control
[scrollview scrollRectToVisible:CGRectMake(320, 80,320, 350) animated:YES];