UIButton animation in the press

I am trying to write my own subclass UIButton

that will "animate" when pressed and released.

When pressed, the button should "shrink" (towards its center) to 90% of its original size. On release, the button should "expand" to 105%, shrink again to 95%, and then return to its original size.

Here is the code I got right now:

#pragma mark -
#pragma mark - Touch Handling
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event];
    [self animatePressedDown];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesCancelled:touches withEvent:event];
    [self animateReleased];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];
    [self animateReleased];
}

- (void)animatePressedDown {
    NSLog(@"button.frame before animatedPressedDown: %@", NSStringFromCGRect(self.frame));
    [self addShadowLayer];
    CATransform3D ninetyPercent = CATransform3DMakeScale(0.90f, 0.90f, 1.00f);
    [UIView animateWithDuration:0.2f
                     animations:^{
                         self.layer.transform = ninetyPercent;
                     }
                     completion:^(BOOL finished) {
                         NSLog(@"button.frame after animatedPressedDown: %@", NSStringFromCGRect(self.frame));
                     }
     ];
}
- (void)animateReleased {
    [self.shadowLayer removeFromSuperlayer];
    CATransform3D oneHundredFivePercent = CATransform3DMakeScale(1.05f, 1.05f, 1.00f);
    CATransform3D ninetyFivePercent     = CATransform3DMakeScale(0.95f, 0.95f, 1.00f);
    [UIView animateWithDuration:0.1f
                     animations:^{
                         self.layer.transform = oneHundredFivePercent;
                     }
                     completion:^(BOOL finished) {
                         NSLog(@"button.frame after animateReleased (Stage 1): %@", NSStringFromCGRect(self.frame));
                         [UIView animateWithDuration:0.1f
                                          animations:^{
                                              self.layer.transform = ninetyFivePercent;
                                          }
                                          completion:^(BOOL finished) {
                                              NSLog(@"button.frame after animateReleased (Stage 2): %@", NSStringFromCGRect(self.frame));
                                              [UIView animateWithDuration:0.1f
                                                               animations:^{
                                                                   self.layer.transform = CATransform3DIdentity;
                                                                   self.layer.frame     = self.frame;
                                                               }
                                                               completion:^(BOOL finished) {
                                                                   NSLog(@"button.frame after animateReleased (Stage 3): %@", NSStringFromCGRect(self.frame));
                                                               }
                                               ];
                                          }
                          ];
                     }
     ];
}

      

Anyway, the above code works great ... for a while. In other cases, the button animation works as expected, but after the animation is "released", the final frame of the button is "pushed" up and out of its original position. This is why I have those operators NSLog

to keep track of exactly where the button frame is at each stage of the animation. When a "shift" occurs, it is somewhere between animatePressedDown

and animateReleased

. At least the frame shown in animatePressedDown

is ALWAYS what I expect, but the first value for the frame in animateReleased

is often wrong.

I do not see the madness pattern, although the same buttons in my application behave correctly or incorrectly sequentially from application launch to application.

I use an automaton for all of my buttons, so I can't figure out what the difference is, so that one button behaves correctly and the other changes its position.

+3


source to share


2 answers


I really hate it when I answer my own questions, but I figured it out.

(I wrestled with this issue for a few days, but finally clicked after I asked a question. Isn't that how it works?)

Apparently the problem for me was that (on buttons that were "offset") I was changing the average animation of the button title.

Once I set up the block-based system to call the name change only after the bounce / pop button animation was complete, the problems went away.



I still don't know why it works the way it does, since the headers I was customizing didn't change the overall size of the buttons in any way, but setting the buttons up as described fixed my problem.

Here's the code from my custom subclass UIButton

with the block property added:

@interface MSSButton : UIButton {

}
@property (copy  , nonatomic) void(^pressedCompletion)(void);
// Other, non-related stuff...
@end

@implementation MSSButton
#pragma mark -
#pragma mark - Touch Handling
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event];
    [self animatePressedDown];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesCancelled:touches withEvent:event];
    [self animateReleased];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];
    [self animateReleased];
}

- (void)animatePressedDown {
    [self addShadowLayer];
    CATransform3D ninetyPercent = CATransform3DMakeScale(0.90f, 0.90f, 1.00f);
    [UIView animateWithDuration:0.2f
                     animations:^{
                         self.layer.transform = ninetyPercent;
                     }
     ];
}

- (void)animateReleased {
    [self.shadowLayer removeFromSuperlayer];
    CATransform3D oneHundredFivePercent = CATransform3DMakeScale(1.05f, 1.05f, 1.00f);
    CATransform3D ninetyFivePercent     = CATransform3DMakeScale(0.95f, 0.95f, 1.00f);
    [UIView animateWithDuration:0.1f
                     animations:^{
                         self.layer.transform = oneHundredFivePercent;
                     }
                     completion:^(BOOL finished) {
                         [UIView animateWithDuration:0.1f
                                          animations:^{
                                              self.layer.transform = ninetyFivePercent;
                                          }
                                          completion:^(BOOL finished) {
                                              [UIView animateWithDuration:0.1f
                                                               animations:^{
                                                                   self.layer.transform = CATransform3DIdentity;
                                                               }
                                                               completion:^(BOOL finished) {
                                                                   if (self.pressedCompletion != nil) {
                                                                       self.pressedCompletion();
                                                                       self.pressedCompletion = nil;
                                                                   }
                                                               }
                                               ];
                                          }
                          ];
                     }
     ];
}
@end

      

Since mine IBAction

fires before animateReleased

(due to the order of the methods listed in my touch handling methods), I just set the block pressedCompletion

to mine IBAction

and the title changes at the end of the animation.

+3


source


The likely problem is that you are trying to use the view property frame

after changing the property transform

. The link to UIView

specifically warns of this :

Warning. If the conversion property is not an identity conversion, the value of that property is undefined and should therefore be ignored.



Specifically, you change the layer transform

that probably affects the view transform

and then access to self.frame

. You can be sure that before you can view self.frame

before submitting the transform

set CGAffineTransformIdentity

.

0


source







All Articles