I've been banging my head over what I thought would have been an easy thing. A pulsing circle, there are several example on SO, but I'm still unable to fix one last thing.
With the code below, I do have a circle that expand and contracts fine, but during contraction, the scaling is done from the CGRect's layer frame (upper left corner). Expansion works fine, and both expansion/contraction work fine on iOS8. The problem only happens on iOS7.
I tried with setting the anchor point, translating the view while expanding ... nothing seemed to do the trick... I'm doing something wrong, I don't know what.
If anyone want to try with the code below you just need a UIViewController, a label, and a button, wired to the IBOutlets below.
That's my last - known - bug before I can submit my app to the app Store... :\
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *label;
@property (weak, nonatomic) IBOutlet UIButton *button;
@property (nonatomic) BOOL isExpanded;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.isExpanded = YES;
self.label.layer.cornerRadius = self.label.frame.size.width/2;
self.label.layer.masksToBounds = YES;
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
}
- (IBAction)ExpandOrCollapse:(id)sender {
[self.button setEnabled:NO];
CGFloat animationDuration = 0.30f; // Your duration
CGFloat animationDelay = 0.0f; // Your delay (if any)
if (self.isExpanded) {
CGAffineTransform t = CGAffineTransformMakeScale(.17f, .17f);
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
scaleAnimation.duration = animationDuration;
scaleAnimation.fromValue = [NSNumber numberWithFloat:1.0f];
scaleAnimation.toValue = [NSNumber numberWithFloat:0.17f];
[self.label.layer addAnimation:scaleAnimation forKey:@"scale"];
[UIView animateWithDuration:animationDuration
delay:animationDelay
options: UIViewAnimationOptionCurveEaseIn
animations:^{
}
completion:^(BOOL finished){
self.isExpanded = NO;
[self.label setTransform:t];
[self.button setEnabled:YES];
}];
}else{
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
scaleAnimation.duration = animationDuration;
scaleAnimation.fromValue = [NSNumber numberWithFloat:0.17f];
scaleAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[self.label.layer addAnimation:scaleAnimation forKey:@"scale"];
[UIView animateWithDuration:0.3
delay:0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
}
completion:^(BOOL finished){
[self.button setEnabled:YES];
[self.label setTransform:CGAffineTransformMakeScale(1.0, 1.0f)];
self.isExpanded = YES;
}];
}
}
@end
EDIT
for eiran, here's the code with his modifications. Problem is still present unfortunately..
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *label;
@property (weak, nonatomic) IBOutlet UIButton *button;
@property (nonatomic) BOOL isExpanded;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.isExpanded = YES;
self.label.layer.cornerRadius = self.label.frame.size.width/2;
self.label.layer.masksToBounds = YES;
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
}
- (IBAction)ExpandOrCollapse:(id)sender {
[self.button setEnabled:NO];
CGFloat animationDuration = 0.30f; // Your duration
CGFloat animationDelay = 0.0f; // Your delay (if any)
if (self.isExpanded) {
[UIView animateWithDuration:animationDuration
delay:animationDelay
options: UIViewAnimationOptionCurveEaseIn
animations:^{
[self.label setTransform:CGAffineTransformScale(CGAffineTransformIdentity, 0.17f, 0.17f)];
}
completion:^(BOOL finished){
self.isExpanded = NO;
[self.button setEnabled:YES];
}];
}else{
[UIView animateWithDuration:0.3
delay:0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
[self.label setTransform:CGAffineTransformScale(CGAffineTransformIdentity, 1.0f, 1.0f)];
}
completion:^(BOOL finished){
[self.button setEnabled:YES];
self.isExpanded = YES;
}];
}
}
This seemed to work for me:
[UIView animateWithDuration:0.75
delay:0
options:UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse|UIViewAnimationOptionAllowUserInteraction
animations:^{
self.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.95, 0.95);
}completion:^(BOOL b){
}];
that's a repeating scale animation.
EDIT: OK then:) apple works in mysterious ways. i've found a solution for you, but i'm not sure what the problem is. in ios 7.1, if you work with a storyboard, the scaling does what you say. BUT, if you add a component dynamically, the animation works fine. add this to your code and check it out:
- (void)viewDidLoad {
[super viewDidLoad];
self.isExpanded = YES;
UIButton* b = [UIButton buttonWithType:UIButtonTypeCustom];
[b setFrame:CGRectMake(100, 400, 120, 40)];
[b addTarget:self action:@selector(expand:) forControlEvents:UIControlEventTouchUpInside];
[b setBackgroundColor:[UIColor blueColor]];
[b setTitle:@"added dynamically" forState:UIControlStateNormal];
[self.view addSubview:b];
}
-(void)expand:(UIButton*)sender{
CGFloat animationDuration = 0.30f; // Your duration
CGFloat animationDelay = 0.0f; // Your delay (if any)
if (self.isExpanded) {
[UIView animateWithDuration:animationDuration
delay:animationDelay
options: UIViewAnimationOptionCurveEaseIn
animations:^{
[sender setTransform:CGAffineTransformScale(CGAffineTransformIdentity, 0.8f, 0.8f)];
}
completion:^(BOOL finished){
self.isExpanded = NO;
}];
}else{
[UIView animateWithDuration:0.3
delay:0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
[sender setTransform:CGAffineTransformScale(CGAffineTransformIdentity, 1.0f, 1.0f)];
}
completion:^(BOOL finished){
self.isExpanded = YES;
}];
}
}
as you said, it was fixed on ios 8. i thought it might have something to do with constraints, but it looks like it doesn't.
added a project that demonstrates this test here: the test
hope that helps, eiran.