CGAffineTransformMakeScale animation on a cornerRadius rounded UIButton
Asked Answered
D

2

6

I am rounding a UIBUtton, which is fine (self is a uibutton subclass):

self.layer.cornerRadius = self.frame.size.width/2;
self.layer.masksToBounds = YES;
self.clipsToBounds = YES;

But I am also trying to animate the button to shrink the scale then return to original size, like so:

[UIView animateWithDuration:0.1 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
    self.layer.affineTransform = CGAffineTransformMakeScale(0.0f, 0.0f);
} completion:^(BOOL finished) {
    [UIView animateWithDuration:0.1 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        self.layer.affineTransform = CGAffineTransformMakeScale(1.0f, 1.0f);
    } completion:nil];
}];

The shrink/restore animation happens as it should. However, i lose the corner radius afterwards and the button goes back to a square. How can I animate and change the transform scale while maintaining the circular button?

I have also tried the following. It restores the button back to circular state at the end of the animation, but there is a fraction of a second where it boxes back to a square at the very beginning of the animation and it looks pretty bad:

[UIView animateWithDuration:0.1 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
    self.layer.affineTransform = CGAffineTransformMakeScale(0.0f, 0.0f);
    self.layer.cornerRadius = 0.0f;
    self.layer.masksToBounds = YES;
    self.clipsToBounds = YES;
} completion:^(BOOL finished) {
    [UIView animateWithDuration:0.1 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        self.layer.affineTransform = CGAffineTransformMakeScale(1.0f, 1.0f);
        self.layer.cornerRadius = self.frame.size.width/2;
        self.layer.masksToBounds = YES;
        self.clipsToBounds = YES;
    } completion:nil];
}];

EDIT: I need the animation code and the rounding the button code, to all happen within the subclassed button. It needs to happen internally from the button class when the button is touched (ie, no code for rounding the button or calling the animation can be in the view controller).

EDIT WITH CLARIFIED SOLUTION: I have no idea why this worked, but a portion of the below suggestion seemed to fix the problem. I was adding the animation method as an action on the custom button for UIControlEventTouchDown. I removed the control event action and instead called the animation method from -(void)touchesBegan.... inside the subclassed button class and everything seems to be working fine. Not sure why this happened. Would love further clarification/explanation, in a comment or something, if someone knows why this worked. Thanks again to @Shan for the help

Depressomotor answered 2/1, 2014 at 22:10 Comment(0)
R
5

Try by putting the animation part in controller class not in subclassed button class.


 //in customButton.h file

 #import <UIKit/UIKit.h>

 @interface customButton : UIButton

 - (id)initWithFrameOfCustomButton:(CGRect)frame;
 - (void)animate;//this method u need to add and call it from the controller
@end




 //in the subclassed UIButtin class
 //in customButton.m file


 #import "customButton.h"
 #import <QuartzCore/QuartzCore.h>

 @implementation customButton

 - (id)initWithFrame:(CGRect)frame
 {
    self = [super initWithFrame:frame];
    if (self) {
     // Initialization code

    }
      return self;
 }

 - (id)initWithFrameOfCustomButton:(CGRect)frame
  {
    self = [super initWithFrame:frame];
    if(self)
    {
     //hear only set the frame only not incude animation part
     //this is the custom initiliser that initilises the custom button with the given frame and also make it to round
     self.frame = frame;
     self.backgroundColor = [UIColor greenColor];
     self.layer.cornerRadius = frame.size.width/2;
     self.layer.masksToBounds = YES;
    }
     return self;
  }

  //add the animation part
 - (void)animate //this method is called within the this class
 {

    [UIView animateWithDuration:0.1 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
     self.layer.affineTransform = CGAffineTransformMakeScale(0.0f, 0.0f);
   //edit: comment below lines becz u already made it to round
   //  self.layer.cornerRadius = 0.0f;
   //  self.layer.masksToBounds = YES;
   //  self.clipsToBounds = YES;

     //        CATransform3D t = CATransform3DIdentity;
     //        t = CATransform3DMakeScale(0 , 0, 1.0f);
     //        cButton.layer.transform = t;
 } completion:^(BOOL finished) {
     [UIView animateWithDuration:0.1 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        self.layer.affineTransform = CGAffineTransformMakeScale(1.0f, 1.0f);
      //  edit comment below lines  
      //  self.layer.cornerRadius = self.frame.size.width/2;
      //  self.layer.masksToBounds = YES;
      //  self.clipsToBounds = YES;
        //            CATransform3D t = CATransform3DIdentity;
        //            t = CATransform3DMakeScale(1 , 1, 1.0f);
        //            cButton.layer.transform = t;
     } completion:nil];
   }];

  }

   //implement touch handling methods to call animation 
 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
  {
     [self animate];
  }

  - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
  {
     [self animate];
  }

 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
 {
    // [self animate]; 
 }

 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
 {
    // [self animate];
 }


in the controller class include the animation part like below


  //in the view controller that u are using this custom button 

  #import "FirstViewController.h"
  #import "CustomCell.h"
  #import "customButton.h" 
  #import <QuartzCore/QuartzCore.h>

  @interface FirstViewController ()
  @end

  @implementation FirstViewController

  - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
 {
      self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
      if (self) {
       // Custom initialization
      }
     return self;
 }

 - (void)viewDidLoad
  {
      [super viewDidLoad];
      // Do any additional setup after loading the view from its nib.
      customButton *cButton = [[customButton alloc]initWithFrameOfCustomButton:CGRectMake(20, 30, 100, 100)]; //hear i am initilising the custom button
      cButton.tag = 100;//assigning tag to access during the animation
      [self.view addSubview:cButton];
     // [self makeAnimation];//i am animation rite after it loads the all views u can place this method call where ever u want 
    //edit: i am commenting the this line so animations of the button in this class won't call
   }

//edit below method is added 
 - (void)viewDidAppear:(BOOL)animated 
 {
     customButton *cButton = (customButton *)[self.view viewWithTag:100];
     //edit->comment this line so that u never call the animation from view controller
    // [cButton animate];//animating the button the code in the subclassed method is called
 }



 - (void)makeAnimation 
 {
      customButton *cButton = (customButton *)[self.view viewWithTag:100];//get the custom button by using the tag 

      [UIView animateWithDuration:0.1 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
    cButton.layer.affineTransform = CGAffineTransformMakeScale(0.0f, 0.0f);
   // cButton.layer.cornerRadius = 0.0f;
   // cButton.layer.masksToBounds = YES;
   // cButton.clipsToBounds = YES;

 //        CATransform3D t = CATransform3DIdentity;
 //        t = CATransform3DMakeScale(0 , 0, 1.0f);
 //        cButton.layer.transform = t;
  } completion:^(BOOL finished) {
        [UIView animateWithDuration:0.1 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        cButton.layer.affineTransform = CGAffineTransformMakeScale(1.0f, 1.0f);
     //   cButton.layer.cornerRadius = cButton.frame.size.width/2;
     //   cButton.layer.masksToBounds = YES;
     //   cButton.clipsToBounds = YES;
 //            CATransform3D t = CATransform3DIdentity;
 //            t = CATransform3DMakeScale(1 , 1, 1.0f);
 //            cButton.layer.transform = t;
        } completion:nil];
   }];


 }


note UIButton inherits from: UIControl -> UIView -> UIResponder -> NSObject see docs if u are confused if you want use UIControlers i.e responder then u need to implement these methods you can get the event for which u want,

in your subclassed UIButton class implement this by commenting touch handling methods

  - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
    {
      return YES;
    }

  - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
    {

    }

 - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
   {

     return YES;
   }
Ramiform answered 3/1, 2014 at 5:12 Comment(12)
thanks for the suggestion. I need the animations to stay in the button class though. I'll give this a shot and check the results, if it works, maybe it will help to pinpoint what particularly isn't working while the code is in the button classDepressomotor
u can also put animation part in the subclassed button class u need to call it from the controller class i will edit the codeRamiform
check out the edited answer and also see the animation part and comment settinng the corner radius in the animation block it will works fineRamiform
sorry I went MIA, had to leave for a trip and have been bogged down. I'm still on the road, but I will check this as soon as I get a chance. Thanks again for taking the time to answer. Will get back to you soon.Depressomotor
This (as well as my original code) works as long as the call to animate comes from outside the custom button class. The problem is that I need the call to start the animation to come from within the custom button class, so it happens automatically on a touch event. Our code is basically the same to animate the button, but the problem (i.e., the reason for the SO question) is how to get this to work from within the custom button class. I really appreciate you taking a crack at it though.Depressomotor
i updated the code pls check, by implementing the touch handling methods inside the subclass may solve your problem,.. hope this helps ... :)Ramiform
I was adding the animation method as an action for the control event TouchDown... I changed it and called the animate method from touchesBegan (as in your edit) and everything works fine now. So strange. Thanks for your help, I really appreciate it :) P.S. any idea why calling animate in touchesBegan works and why it gets screwed up as a button action for UIControlEventTouchDown?Depressomotor
because UIButton is subclass of UIView therefore u can use all the methods related to UIView ... :)Ramiform
Hi, I know it is an old answer, but what can I do to prevent the animation modifies the origin point of the view?Rf
@LucaD : it doesn't change origin point of the view in the above case button, only it scales from centerRamiform
Shan, the problem was that I did not modify the anchor point that by default is on center point, so the origin was changing while scalingRf
@LucaD : no it should not change the origin, i tested it now and before replaying to you, it just stays in the original location and scales from center (default anchor point) and returned to original frame.. check it onceRamiform
S
2

Similar to Shankar BS answer, but with swift 3. Transformation with bounced animation:

  myButton.layer.setAffineTransform(CGAffineTransform(scaleX: 0.0, y: 0.0))

            UIView.animate(withDuration: 0.7, delay: 1.0,
                            usingSpringWithDamping: 0.7, initialSpringVelocity: 7.0, options: [],
                            animations:  {
                            myButton.layer.setAffineTransform(CGAffineTransform(scaleX: 1.0, y: 1.0))
            },
                           completion: nil
            )
Supernova answered 26/10, 2017 at 15:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.