I have a UIView in that, I am going to add 15 UIButtons. If I have written code like this
for(int i =0; i<15;i++)
{
[self.view addSubview:[self.buttonsArray objectAtIndex;i]];
}
But I want to subview UIButtons one after other. something like animation effect.How to achieve it.
try this:
in .h file
NSTimer *timer;
int buttonNo;
#property (nonatomic,retain) NSTimer *timer;
in .m file
#synthesize timer;
-(void) viewDidLoad {
[super viewDidLoad];
//start your timer where you write your for loop
buttonNo = 0;
timer = [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(addbuttons) userInfo:nil repeats:YES];
}
-(void) addbuttons {
[self.view addSubview:[self.buttonsArray objectAtIndex;buttonNo]];
buttonNo++;
if (buttonNo == 15) {
[timer invalidate];
timer = nil;
}
}
I would do it like this:
const float fadein_duration = 0.3f;
const float time_between_fadein = 0.15f;
int index = 0;
for (UIButton* button in self.buttonsArray)
{
[self.view addSubview:button];
button.alpha = 0.0f;
[UIView animateWithDuration:fadein_duration delay:(index * time_between_fadein) options:0 animations:^{
button.alpha = 1.0f;
} completion:^(BOOL finished) {
// Do something maybe?
}];
index++;
}
Currently, the animation is to fade-in, but you should be able to adapt it to other types of animations.
There are 2 constants right now which you can use to control the duration of the fadein, and the time between the appearance of 2 buttons.
Make sure you set the correct frames for the buttons when you create them. Otherwise, you will just get a bunch of overlapping buttons. If you need you can set the position of the buttons dynamically when you add them to the subview.
Also, I changed the loop mode to use a fast iterator over the array, and keep a incremental index manually.
Im Unsure of what you mean. As for annimation EFFECT, you may want to implement some type of timer, otherwise they will all be added at the same time
If you want a fade in kind of animation then you can achieve something like this -
for(int i =0; i<15;i++)
{
[self.view addSubview:[self.buttonsArray objectAtIndex;i]];
[self performSelector:#selector(showBtn:) withObject:self.buttonsArray objectAtIndex;i] afterDelay:interval];
interval += 0.5f;
}
- (void) showBtn:(UIButton*) btn
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5f];
[btn setAlpha:1.0f];
[UIView commitAnimations];
}
i assume the default alpha of all buttons in array is 0.
pls try this code
for(int i =0; i<15;i++)
{
[self.view addSubview:[self.buttonsArray objectAtIndex;i]];
[self performSelector:#selector(your animated methode) withObject:nil afterDelay:0.1];
}
Related
I implemented corePlot in my xcode project. I'm trying to "explode" a slice of the pie chart with animation. Here is the method I'm using:
- (void)radialOffsetForPieChart:(CPTPieChart *)pieChart recordIndex:(NSUInteger)idx
{
if (myIndex == idx) {
return 20;
}
return 0;
}
I have another method which calls [pieChart reloadRadialOffset];.
For example:
- (void)thisMethod {
[pieChart reloadRadialOffset];
}
How can I animate the reloadRadialOffsets?
I just added an example to the "Simple Pie Chart" demo in the Plot Gallery example app. I added two properties to the controller to hold the index of the selected slice and the desired offset value. Since the offset is a CGFloat it is easily animated using Core Animation or CPTAnimation.
Set the index to NSNotFound to indicate that no slice should be selected. You could also use an array or set of indices if you want to highlight more than one slice at a time.
self.offsetIndex = NSNotFound;
Trigger the animation to offset the slice:
self.offsetIndex = idx;
[CPTAnimation animate:self
property:#"sliceOffset"
from:0.0
to:35.0
duration:0.5
animationCurve:CPTAnimationCurveCubicOut
delegate:nil];
The plot datasource needs the radial offset method:
-(CGFloat)radialOffsetForPieChart:(CPTPieChart *)pieChart recordIndex:(NSUInteger)index
{
return index == self.offsetIndex ? self.sliceOffset : 0.0;
}
The sliceOffset property needs a custom setter to trigger the plot to update the offsets during the animation:
-(void)setSliceOffset:(CGFloat)newOffset
{
if ( newOffset != sliceOffset ) {
sliceOffset = newOffset;
[self.graphs[0] reloadData];
}
}
In your question is bit confusing. Your question title says "how to reaptedly call a mathod using timer" and at the end of your question it changes to "How can I animate the reloadRadialOffsets?".
For repeatedly calling a method you can use any of the following options
option 1.
[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(animatedPie:)
userInfo:nil
repeats:YES];
option 2:
[self performSelector:#seletor(animatePie) withObject:nil afterDelay:1.0];
and in your method
-(void) animatePie
{
[UIView animateWithDuration:1.0 animations:^
{
[pieChart reloadRadialOffsets];
} completion:^(BOOL finished)
{
[self performSelector:#seletor(animatePie) withObject:nil afterDelay:1.0];
}];
}
Here the repeated method will be called agian with a delay once the animation is complete.
When you wanted to stop the animation call
- (void)cancelPerformSelector:(SEL)aSelector target:(id)target argument:(id)arg
When timer is used it will be fired once the interval is elapsed.
For animation
You can use
[UIView animateWithDuration:1.0 animations:^
{
} completion:^(BOOL finished) {
}];
or user
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:key];
You can use below code. In this UIViewAnimationOptionRepeat is helpful for repeat animation what you want to achieve..
[UIView animateWithDuration:5
delay:1
options:UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionRepeat
animations:^{
[UIView setAnimationRepeatCount:2];
[pieChart reloadRadialOffsets];
}
completion:nil];
//Still you want to call method using timer
NSTimer *animateTimer;
animateTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self
selector:#selector(thisMethod) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:animateTimer forMode:NSDefaultRunLoopMode];
[animateTimer fire];
- (void)thisMethod {
[pieChart reloadRadialOffsets];
}
Hope it helps you...!
I have a UISlider and I am trying to add subviews dynamically on the track of the UISlider with animation. I need 1 second time delay before adding each subview. How can I achieve this?
This is a basic setup of how you can go about achieving it:
#interface ViewController () {
NSTimer *_timer;
CGRect sliderFrame;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.slider.minimumValue = 0.0f;
self.slider.maximumValue = 100.0f;
sliderFrame = self.slider.frame;
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(addLabel:) userInfo:nil repeats:YES];
[_timer fire];
}
- (void) addLabel:(NSTimer*) timer {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(sliderFrame.origin.x + self.slider.value/100 * self.slider.frame.size.width, sliderFrame.origin.y - 10, 20, 20)];
label.text = #"1";
[self.view addSubview:label];
}
#end
Assuming you are using an animation block, then, within your block you can use setAnimationDelay. For example;
[UIView animateWithDuration:5.0 animations:
^{
[UIView setAnimationDelay:1.0];
...
} completion:^(BOOL finished){
...
}];
This would do a 5 second animation with a delay of 1 second before it started.
you can hook up the valueChangeEvent with an action method.
You can put an animation block in that method with passing the seconds to wait before starting an animation as delay parameter.
[UIView animateWithDuration:(NSTimeInterval) delay:(NSTimeInterval) options:(UIViewAnimationOptions) animations:^(void)animations completion:^(BOOL finished)completion]
then you can change add the view with whatever animation your require.
I am trying to get a button to move to random locations inside my view. However, the dot only moves at random when it is pressed. I also want it to fade to a different location if it is not pressed. I used CGRect because I need the dot to stay within the bounds of a specific view.
-(IBAction)randomRed:(id)sender
{
[self redDot];
CGRect senderFrame = [sender frame];
CGRect superBounds = [[sender superview ] bounds];
senderFrame.origin.x = (superBounds.size.width - senderFrame.size.width) * drand48();
senderFrame.origin.y = (superBounds.size.height - senderFrame.size.height) * drand48();
[sender setFrame:senderFrame];
counter = counter - 5;
scoreLabel.text = [NSString stringWithFormat:#"Points %i", counter];
NSString *path = [[NSBundle mainBundle] pathForResource:#"Tapadot Red Dot Buzzer Short" ofType:#"mp3"];
sound = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
sound.numberOfLoops = 1;
[sound play];
[self subtractOne];
[self performSelector:#selector(showRedDot) withObject:nil afterDelay:1.0];
}
-(void)startRedDot
{
if (counter ==0)
redDot = [NSTimer scheduledTimerWithTimeInterval:4.0
target:self
selector:#selector(showRedDot)
userInfo:nil
repeats:YES];
}
-(void)showRedDot
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[redButton setAlpha:1];
[UIView commitAnimations];
[self performSelector:#selector(redDot) withObject:nil afterDelay:1.0];
}
-(void)redDot
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
[redButton setAlpha:0];
[UIView commitAnimations];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self setUpGame];
[self sizing];
}
Currently your random positioning code is only called when the button is pressed.
To get it to move Independent of button presses we should put it in its own method, which can invoke itself after a set duration.
First we need to get a reference to the instance of the button you want to move so we can manipulate it.
Then lets separate the movement code into its own method.
- (void) moveButtonRandomly {
CGSize limits;
CGPoint newPosition;
// Get limits
limits.width = self.view.frame.size.width - button.frame.size.width;
limits.height = self.view.frame.size.height - button.frame.size.height;
// Calculate new Position
newPosition = (CGPoint){ limits.width * drand48(), limits.height * drand48() };
// Set new frame
button.frame = (CGRect){ newPosition, button.frame.size };
}
Change your startRedDot to this, which will construct, and start the timer.
- (void) startRedDot {
redDotTimer = [NSTimer scheduledTimerWithTimeInterval:4.0
target:self
selector:#selector(moveButtonRandomly)
userInfo:nil
repeats:YES];
[redDotTimer fire];
}
The animation is rather simple to add just create a new method which invokes two animations; One which fades out the button with a completion that moves the button, and fires off the next animation to fade it back in.
- (void) moveButtonWithAnimation {
CGFloat fadeDurration;
if ( !button )
return; // only invoke the button if it exists
fadeDurration = 0.4;
//Fade Out
[UIView animateWithDuration: fadeDurration animations:^{
button.alpha = 0.0f;
} completion:^(BOOL finished) {
// Move the button while it is not visible
[self moveButtonRandomly];
// Fade in
[UIView animateWithDuration: fadeDurration animations:^{
button.alpha = 1.0f;
}];
}];
}
Finally Edit the timers selector to be the animation method.
- (void) startRedDot {
redDotTimer = [NSTimer scheduledTimerWithTimeInterval:4.0
target:self
selector:#selector(moveButtonWithAnimation)
userInfo:nil
repeats:YES];
[redDotTimer fire];
}
Trying to get to grips with Xcode and seem to be making some progress over the last few weeks.
Does anyone know a way that a custom button can do a different set of animations on a second click.
So let say I have a custom button and its of Mario, when i click it he runs from the middle of the screen and out of the right hand side of the screen and then runs back in to the middle from the left of the screen, he also makes noise.
I have achieved this using this code:
- (IBAction)marioRunning_clicked:(id)sender
{
[UIView animateWithDuration:1.50 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
marioRunning.center = CGPointMake(350.5, 456.0);
} completion:^(BOOL finished) {
if (finished) {
[UIView animateWithDuration:0.00 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
marioRunning.center = CGPointMake(-30.5, 456.0);
} completion:^(BOOL finished) {
if (finished) {
[UIView animateWithDuration:1.50 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
marioRunning.center = CGPointMake(160.0, 456.0);
} completion:^(BOOL finished) {
if(finished) // NSLog ( #"Finished !!!!!" );
marioRunning.center = CGPointMake(160.0, 456.0);
}];
}
}];
}
}];
marioRunning.imageView.animationImages = [NSArray arrayWithObjects:[UIImage imageNamed:#"mario-running2"],[UIImage imageNamed:#"mario-running3"],nil];
marioRunning.imageView.animationDuration = 0.15;
marioRunning.imageView.animationRepeatCount = 19;
[marioRunning.imageView startAnimating];
}
How could I make him do a second set of animations on the next click? For example instead of running from left to right, if i tap him the second time he will Jump up and down?
Use the selected state of the button to decide which animation to do
-(void)buttonClicked:(UIButton*)button
{
if(button.selected)
{
[self doAnimation1];
}
else
{
[self doAnimation2];
}
button.selected = !button.selected;
}
This might be overkill, but here's a cool way to do what you want:
Make a subclass of UIButton, and let's call it DDDMarioButton:
typedef void (^DDDAnimationBlock)(UIButton *button);
#interface DDDMarioButton : UIButton
- (void)addAnimationBlockToQueue:(DDDAnimationBlock)block;
#end
Then in DDDMarioButton.m:
#interface DDDMarioButton ()
#property (nonatomic, strong) NSMutableArray *animationQueue;
#end
#implementation DDDMarioButton
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
- (void)buttonPressed:(id)button
{
DDDAnimationBlock block = self.animationQueue[0];
block(self);
[self.animationQueue removeObjectAtIndex:0];
}
- (void)addAnimationBlockToQueue:(DDDAnimationBlock)block
{
if(!self.animationQueue)
{
self.animationQueue = [NSMutableArray new];
}
[self.animationQueue addObject:block];
}
#end
and then wherever you create your buttons, you add each step one by one:
DDDMarioButton *button = [[DDDMarioButton alloc] initWithFrame:CGRectZero];
[button addAnimationBlockToQueue:^(UIButton *button) {
// perform some animation
}];
[button addAnimationBlockToQueue:^(UIButton *button) {
// perform another animation
}];
And that should do it. I haven't tested this, you'll probably need some configuration, but that's pretty much the idea.
I'm trying to set the progress of a UIProgressView, but the progressView doesn't "update".
Why? Do you know what I am doing wrong?
Here is my code in ViewController.h:
IBOutlet UIProgressView *progressBar;
That's the code in ViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
progressBar.progress = 0.0;
[self performSelectorOnMainThread:#selector(progressUpdate) withObject:nil waitUntilDone:NO];
}
-(void)progressUpdate {
float actual = [progressBar progress];
if (actual < 1) {
progressBar.progress = actual + 0.1;
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self `selector:#selector(progressUpdate) userInfo:nil repeats:NO];`
}
else {
}
}
Try calling [progressBar setProgress:<value> animated:YES]; instead of progressBar.progress = <value>
EDIT: Take a look at this, which looks a lot like your example: Objective c : How to set time and progress of uiprogressview dynamically?
Double check that progressBar is linked correctly in Interface Builder.
If it still doesn't work, try using [progressBar setProgress:0]; and [progressBar setProgress:actual+0.1];.
Also know that the UIProgressView has a [progressBar setProgress:<value> animated:YES]; method. May look cleaner.
Its should look like this:
- (void)viewDidLoad
{
[super viewDidLoad];
progressBar.progress = 0.0;
[self performSelectorInBackground:#selector(progressUpdate) withObject:nil];
}
-(void)progressUpdate {
for(int i = 0; i<100; i++){
[self performSelectorOnMainThread:#selector(setProgress:) withObject:[NSNumber numberWithFloat:(1/(float)i)] waitUntilDone:YES];
}
}
- (void)setProgress:(NSNumber *)number
{
[progressBar setProgress:number.floatValue animated:YES];
}
I had the same problem this morning. It appeared that the progressBar was partly over a UITableView. I moved it a little, so that it was not overlapping anymore.
I'm not sure why this is. It should have been working in the original case, but that is how I solved it.