Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CGAffineTransformMakeScale animation on a cornerRadius rounded UIButton

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

like image 908
Daniel McCarthy Avatar asked Jan 02 '14 22:01

Daniel McCarthy


2 Answers

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;
   }
like image 91
Shankar BS Avatar answered Oct 17 '22 03:10

Shankar BS


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
            )
like image 25
Illya Krit Avatar answered Oct 17 '22 03:10

Illya Krit