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.
Related
is it possible to put animation to UITableView?
I currently have this:
- (IBAction)mPesoAccount:(id)sender {
if (accountBalanceTableView.isHidden) {
accountBalanceTableView.hidden = NO;
} else {
accountBalanceTableView.hidden = YES;
}
}
it hides or show the uitableview whenever i click a button, i would like to add an animation when i'm hiding and showing the uitableview
it would similar like a dropdown item, where it slides up into the button and collapses down when the button clicked again.
Simply animating your UITableView frame if you are using non constraints approach, or making the bottom constraint IBOutlet and changing the constant value
- (void)viewDidLayoutSubViews
{
[super viewDidLayoutSubViews];
self.originalTableViewFrame = accountBalanceTableView.frame;
}
- (IBAction)mPesoAccount:(id)sender {
if (accountBalanceTableView.isHidden)
{
[UIView animateWithDuration:.3 animations:^{
accountBalanceTableView.frame = self.originalTableViewFrame;
}completion:^(BOOL finished) {
accountBalanceTableView.hidden = NO;
}];
}
else
{
[UIView animateWithDuration:0.3 animations:^{
accountBalanceTableView.frame = CGRectMake(originalTableViewFrame.origin.x, originalTableViewFrame.origin.y + originalTableViewFrame.size.height, originalTableViewFrame.size.width, 0);
} completion:^(BOOL finished) {
accountBalanceTableView.hidden = YES;
}];
}
}
I am trying to animate a UITextView to expand the to the width of its containing UIToolbar.
But it animates weird, jumping around.
Can anyone help me make understand why my textbox jumps around?
#define ANIMATION_DURATION 0.5
#define ANIMATION_DELAY 0.03
#interface WebViewController () <UITextFieldDelegate>{
CGRect URLBAR_DEFAULT;
}
-(void)viewDidLoad{
self.txtURLBar = [[UITextField alloc] init];
self.txtURLBar.bounds = CGRectMake(0, 0, 100, 30);
self.txtURLBar.delegate = self;
self.txtURLBar.backgroundColor = [UIColor lightGrayColor];
self.txtURLBar.clearButtonMode = UITextFieldViewModeWhileEditing;
URLBAR_DEFAULT = self.txtURLBar.frame; //KEEP A REFRENCE TO THE ORIGINAL SIZE
}
//START EDITING EXPAND TO FULL WIDTH
- (void)textFieldDidBeginEditing:(UITextField *)textField {
[UIView animateWithDuration:ANIMATION_DURATION delay:ANIMATION_DELAY options:UIViewAnimationOptionCurveLinear animations:^{
[_txtURLBar setFrame:CGRectMake(3, URLBAR_DEFAULT.origin.y, self.view.frame.size.width - 10, URLBAR_DEFAULT.size.height)];
} completion:^(BOOL finished) {
}];
}
//END EDITING SHRINK BACK TO SMALL SIZE
- (void)textFieldDidEndEditing:(UITextField *)textField {
[UIView animateWithDuration:ANIMATION_DURATION delay:ANIMATION_DELAY options:UIViewAnimationOptionCurveEaseOut animations:^{
[_txtURLBar setFrame:CGRectMake(URLBAR_DEFAULT.origin.x, URLBAR_DEFAULT.origin.y, URLBAR_DEFAULT.size.width, URLBAR_DEFAULT.size.height)];
} completion:^(BOOL finished) {
}];
}
The problem may be due to the frame being set in textFieldDidBeginEditing, textFieldDidEndEditing is different. I've done the small POC with same code of yours as below. It's working fine.
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UITextField *resizeableTextField;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//START EDITING EXPAND TO FULL WIDTH
- (void)textFieldDidBeginEditing:(UITextField *)textField {
[UIView animateWithDuration:0.5 delay:0.03 options:UIViewAnimationOptionCurveLinear animations:^{
self.resizeableTextField.frame = CGRectMake(CGRectGetMinX(self.resizeableTextField.frame), CGRectGetMinY(self.resizeableTextField.frame), self.resizeableTextField.frame.size.width + 40, CGRectGetHeight(self.resizeableTextField.frame));
} completion:^(BOOL finished) {
}];
}
//END EDITING SHRINK BACK TO SMALL SIZE
- (void)textFieldDidEndEditing:(UITextField *)textField {
[UIView animateWithDuration:0.5 delay:0.03 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.resizeableTextField.frame = CGRectMake(CGRectGetMinX(self.resizeableTextField.frame), CGRectGetMinY(self.resizeableTextField.frame), self.resizeableTextField.frame.size.width - 40, CGRectGetHeight(self.resizeableTextField.frame));
} completion:^(BOOL finished) {
}];
}
#end
I have this problem, i have set in the Storyboard 2 UIButton and after touching one of them they must move, so i implemented this using animation, and works fine. But after the completion of the animation i'm performing a modal segue, the problem is that while modal segue is starting, raising the new UIView from the bottom, the 2 UIButton return to their previous place, where i set them in the Storyboard, and it's orribile to see. How could i solve this?
Here's the code i'm using
#property (strong, nonatomic) IBOutlet UIButton *maleBtn;
#property (strong, nonatomic) IBOutlet UIButton *femaleBtn;
- (void)viewDidLoad
{
[super viewDidLoad];
[_maleBtn addTarget:self
action:#selector(chooseGender:)
forControlEvents:UIControlEventTouchUpInside];
[_femaleBtn addTarget:self
action:#selector(chooseGender:)
forControlEvents:UIControlEventTouchUpInside];
}
- (IBAction)chooseGender:(id)sender
{
CGRect maleFrame;
CGRect femaleFrame;
if (sender == _maleBtn) {
maleFrame = self.maleBtn.frame;
maleFrame.origin.x = (self.view.bounds.size.width/2) - 15;
femaleFrame = self.femaleBtn.frame;
femaleFrame.origin.x = self.view.bounds.size.height;
}
else {
maleFrame = self.maleBtn.frame;
maleFrame.origin.x = -self.view.bounds.size.height;
femaleFrame = self.femaleBtn.frame;
femaleFrame.origin.x = (self.view.bounds.size.width/2) - 15;
}
[UIView animateWithDuration:0.5
delay:0.5
options:UIViewAnimationCurveEaseOut
animations:^{
_maleBtn.frame = maleFrame;
_femaleBtn.frame = femaleFrame;
}
completion:^(BOOL finished){
if(sender == _maleBtn)
[self performSegueWithIdentifier:#"toMale" sender:self];
else
[self performSegueWithIdentifier:#"toFemale" sender:self];
}];
}
At the top you make a bool or add a property in your .h if you like. But I would just use a local variable for this.
BOOL didAnimate;
In viewDidLoad you set it
- (void)viewDidLoad
{
[super viewDidLoad];
//Set the bool value here to no;
didAnimate = NO;
[_maleBtn addTarget:self
action:#selector(chooseGender:)
forControlEvents:UIControlEventTouchUpInside];
[_femaleBtn addTarget:self
action:#selector(chooseGender:)
forControlEvents:UIControlEventTouchUpInside];
}
Use it here, and I also took the liberty to rewrite your code a bit.
- (IBAction)chooseGender:(id)sender
{
CGRect maleFrame;
CGRect femaleFrame;
if (sender == _maleBtn) {
maleFrame = self.maleBtn.frame;
maleFrame.origin.x = (self.view.bounds.size.width/2) - 15;
femaleFrame = self.femaleBtn.frame;
femaleFrame.origin.x = self.view.bounds.size.height;
}
else {
maleFrame = self.maleBtn.frame;
maleFrame.origin.x = -self.view.bounds.size.height;
femaleFrame = self.femaleBtn.frame;
femaleFrame.origin.x = (self.view.bounds.size.width/2) - 15;
}
[UIView animateWithDuration:0.5
delay:0.5
options:UIViewAnimationCurveEaseOut
animations:^{
_maleBtn.frame = maleFrame;
_femaleBtn.frame = femaleFrame;
}
completion:^(BOOL finished){
didAnimate = YES;
}];
if(sender == _maleBtn && didAnimate){
[self performSegueWithIdentifier:#"toMale" sender:self];
}
else if((sender == _femaleBtn && didAnimate)){
[self performSegueWithIdentifier:#"toFemale" sender:self];
}
else{
NSLog(#"failure to transition");
}
}
I have not tested this and there might be spelling errors and such. So copy/paste might not work.
I need to move an UIImageView from x,y to x,y1.
This is what i have. I tried lots of different codes in -(void)animation, most using animateWithDuration but none seems to work. It just doesn't do anything. I must be missing something. Is .h correct? How would you make the image move? I then have to loop that, how can i do that? It's my first time with animate, I can't figure out what to do, it has to be simple. Thanks in Advance.
in .h I connected it this way
#import <UIKit/UIKit.h>
#interface ImageBeaconViewController : UIViewController {
__weak IBOutlet UIImageView *blueView;
}
#end
the in .m I've got
- (void)viewDidLoad
{
[self animation];
[super viewDidLoad];
}
....
- (void)animation{
[UIView animateWithDuration:3 animations:^{
blueView.alpha = 1;
blueView.center = CGPointMake(blueView.center.x , blueView.center.y +??);
}];
}
??= I still have to decide how much I want it to move
This way the app crashes as soon as I get to the screen with the animation
Add Method Like This:
- (void) animateToPoint:(CGPoint)point {
[UIView animateWithDuration:3 animations:^{
blueView.alpha = 1;
blueView.center = point;
}];
}
Call like this:
- (void) viewDidAppear:(BOOL)animated {
int distanceToSlide = 40; // will slide down 40px (use -40 to go up)
CGPoint targetPoint = CGPointMake(blueView.center.x, blueView.center.y + distanceToSlide);
[self animateToPoint:targetPoint];
}
NOTE
Notice that I put your animation call in viewDidAppear if you animate in viewDidLoad you won't see anything because your view isn't displayed yet.
If it's still not moving, make sure "useAutolayout" is not selected check this:
Just because I already built it, try this for tap to animate:
In your viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]init];
tap.numberOfTapsRequired = 1;
[tap addTarget:self action:#selector(handleTap:)];
[self.view addGestureRecognizer:tap];
}
Then, use these:
- (void) handleTap:(UITapGestureRecognizer *)tap {
int distanceToSlide = 40; // will slide down 40px (use -40 to go up)
CGPoint targetPoint = CGPointMake(blueView.center.x, blueView.center.y + distanceToSlide);
[self animateToPoint:targetPoint];
}
- (void) animateToPoint:(CGPoint)point {
[UIView animateWithDuration:3 animations:^{
blueView.alpha = 1;
blueView.center = point;
}];
}
Here's a way to loop it:
#implementation ImageBeaconViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
pointA = CGPointMake(40, 40);
pointB = CGPointMake(250, 350);
blueView.center = pointA;
}
- (void) viewDidAppear:(BOOL)animated {
[self animate];
}
- (void) animate {
if (CGPointEqualToPoint(_blueView.center, pointA)) {
[self animateToPoint:pointB];
}
else {
[self animateToPoint:pointA];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) animateToPoint:(CGPoint)point {
[UIView animateWithDuration:1.0 animations:^{
_blueView.alpha = 1;
_blueView.center = point;
} completion:^(BOOL finished) {
if (finished) {
[self animate];
}
}];
}
#end
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
blueView.alpha = 1;
blueView.center = CGPointMake(blueView.center.x , blueView.center.y + 100);
} completion:^(BOOL finished) {
//if you need any thing after animation is done
}];
this is them code for animation i have
I have a method that drops down a UIView. That works fine. How would I go about having it slide back up when the button is touched a second time?
I also need to deactivate it somehow so that it doesn't repeat the animation if it's already displayed.
- (void)slideDownTableView {
self.tableViewCities = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
self.tableViewCities.frame = CGRectMake(0,0,300,180);
self.tableViewCities.autoresizingMask = ( UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth );
self.tableViewCities.dataSource = self;
self.tableViewCities.delegate = self;
self.tableViewCities.backgroundColor = [UIColor whiteColor];
UIView *myview=[[UIView alloc] initWithFrame: CGRectMake(10, 0,300,180)];
myview.backgroundColor=[UIColor redColor];
[myview addSubview:self.tableViewCities];
[self.view addSubview:myview];
myview.frame = CGRectMake(10, 0,300,-180); // offscreen
[UIView animateWithDuration:0.5
animations:^{
myview.frame = CGRectMake(10, 0,300,180); // final location move
}];
}
iOS provides autoreverse property by which animation reverses automatically.
UIViewAnimationOptionAutoreverse will give you an effect as the animation reverses .
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionAutoreverse
animations:^{
// do whatever animation you want, e.g.,
someView.frame = someFrame1;
}
completion:NULL];
Using BeginFromCurrentState option worked for me.
if(!enabled){
[UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationTransitionCurlUp
animations:^{
myview.frame = CGRectMake(10, 0,300,180);
}
completion:nil];
enabled=YES;
}
else{
[UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationTransitionCurlUp
animations:^{
myview.frame = CGRectMake(10, 0,300,-180);
}
completion:nil];
enabled=NO;
}
With a simple BOOL to store the current state of the sliding UIView you can achieve this.
The initial state is not visible if I understand well? So initially the BOOL citiesVisible is set to N0, then the button's action check its value, and performs a different animation accordingly. Simple.
Finally, I also disable the button during the animation to prevent the user to trigger a second animation while a first one is already performing.
I rewrote a bit your code to follow some best practices, and improve readability/maintainability:
Setting up the views beforehand because the button's action responsibility shouldn't be to setup the views, but only to animate them.
Changed the signature of the target action, that's my personal style guide to state in the sender's name and the ControlEvent, it makes it more tidy when you have multiple buttons/actions ;). Also per Apple guidelines, methods receiving actions should have one "id" parameter, the sender.
Store the frames in variables (you could also have them as #define, that'd be even better I think), so you don't have to hard-code them twice, it reduces the risk of breaking the animation by having a view sliding to different positions...
Code update:
#interface ViewController ()
#property (nonatomic, strong) UIButton *showCitiesButton;
#property (nonatomic, strong) CGRect citiesInitialFrame;
#property (nonatomic, strong) CGRect citiesVisibleFrame;
#property (nonatomic, strong) UITableView *tableViewCities;
#property (nonatomic, strong) UIView *citiesContainer;
#property (nonatomic, strong) BOOL citiesVisible;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Setting up the cities view
self.citiesInitialFrame = CGRectMake(10, -180, 300, 180);
self.citiesVisibleFrame = CGRectMake(10, 0, 300, 180);
self.tableViewCities = [[UITableView alloc] init ...];
self.citiesContainer = [[UIView alloc] init ...];
// ...
self.citiesVisible = NO;
[self.showCitiesButton addTarget:self
action:#selector(showCitiesButtonDidTouchUpInside:)
forControlEvents:UIControlEventTouchUpInside];
}
- (void)showCitiesButtonDidTouchUpInside:(id)sender
{
[self.showCitiesButton setEnabled:NO];
if (self.citiesVisible)
{
// Cities view is visible, sliding it out of the screen
[UIView animateWithDuration:0.5
animations:^{
self.citiesContainer.frame = self.citiesInitialFrame;
} completion:^(BOOL finished) {
self.citiesVisible = NO;
[self.showCitiesButton setEnabled:YES];
}];
}
else
{
// Cities view is not visible, sliding it in
[UIView animateWithDuration:0.5
animations:^{
self.citiesContainer.frame = self.citiesVisibleFrame;
} completion:^(BOOL finished) {
self.citiesVisible = YES;
[self.showCitiesButton setEnabled:YES];
}];
}
}
#end
You can reverse UIView animation with this code:
if (isMarked) {
[UIView animateWithDuration:3
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:(void (^)(void)) ^{
self.transform=CGAffineTransformMakeScale(1.1, 1.1);
}
completion:nil];
} else {
[UIView animateWithDuration:3
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:(void (^)(void)) ^{
self.transform=CGAffineTransformIdentity;
}
completion:nil];
}