Skip to content

Commit 3ae0241

Browse files
samnmajsecord
authored andcommitted
[Snackbar] Fix glitchy dismissal animation (#1166)
* Send correct frame when notifying observers * Redo fix * Remove notificationFrame arg
1 parent 39bc984 commit 3ae0241

File tree

1 file changed

+58
-51
lines changed

1 file changed

+58
-51
lines changed

components/Snackbar/src/private/MDCSnackbarOverlayView.m

Lines changed: 58 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,6 @@ @interface MDCSnackbarOverlayView ()
5454
*/
5555
@property(nonatomic) MDCSnackbarMessageView *snackbarView;
5656

57-
/**
58-
Storage for a completion block that is waiting for a CAAnimation to finish.
59-
*/
60-
@property(nonatomic, copy) void (^pendingCompletionBlock)(void);
61-
6257
/**
6358
The object which will notify us of changes in the keyboard position.
6459
*/
@@ -88,6 +83,16 @@ @interface MDCSnackbarOverlayView ()
8883
*/
8984
@property(nonatomic) NSTimeInterval rotationDuration;
9085

86+
/**
87+
The constraint used to pin the bottom of the snackbar to the bottom of the screen.
88+
*/
89+
@property(nonatomic) NSLayoutConstraint *snackbarOnscreenConstraint;
90+
91+
/**
92+
The constraint used to pin the top of the snackbar to the bottom of the screen.
93+
*/
94+
@property(nonatomic) NSLayoutConstraint *snackbarOffscreenConstraint;
95+
9196
@end
9297

9398
@implementation MDCSnackbarOverlayView
@@ -272,13 +277,26 @@ - (void)setSnackbarView:(MDCSnackbarMessageView *)snackbarView {
272277
}
273278

274279
// Always pin the snackbar to the bottom of the container.
275-
[container addConstraint:[NSLayoutConstraint constraintWithItem:snackbarView
276-
attribute:NSLayoutAttributeBottom
277-
relatedBy:NSLayoutRelationEqual
278-
toItem:container
279-
attribute:NSLayoutAttributeBottom
280-
multiplier:1.0
281-
constant:-bottomMargin]];
280+
_snackbarOnscreenConstraint = [NSLayoutConstraint constraintWithItem:snackbarView
281+
attribute:NSLayoutAttributeBottom
282+
relatedBy:NSLayoutRelationEqual
283+
toItem:container
284+
attribute:NSLayoutAttributeBottom
285+
multiplier:1.0
286+
constant:-bottomMargin];
287+
_snackbarOnscreenConstraint.active = NO; // snackbar starts off-screen.
288+
_snackbarOnscreenConstraint.priority = UILayoutPriorityDefaultHigh;
289+
[container addConstraint:_snackbarOnscreenConstraint];
290+
291+
_snackbarOffscreenConstraint = [NSLayoutConstraint constraintWithItem:snackbarView
292+
attribute:NSLayoutAttributeTop
293+
relatedBy:NSLayoutRelationEqual
294+
toItem:container
295+
attribute:NSLayoutAttributeBottom
296+
multiplier:1.0
297+
constant:-bottomMargin];
298+
_snackbarOffscreenConstraint.active = YES;
299+
[container addConstraint:_snackbarOffscreenConstraint];
282300

283301
// Always limit the height of the snackbar.
284302
[container
@@ -408,40 +426,44 @@ - (void)fadeInsnackbarView:(MDCSnackbarMessageView *)snackbarView
408426
#pragma mark - Slide Animation
409427

410428
- (void)slideMessageView:(MDCSnackbarMessageView *)snackbarView
411-
fromY:(CGFloat)fromY
412-
toY:(CGFloat)toY
429+
onscreen:(BOOL)onscreen
413430
fromContentOpacity:(CGFloat)fromContentOpacity
414431
toContentOpacity:(CGFloat)toContentOpacity
415-
notificationFrame:(CGRect)notificationFrame
416432
completion:(void (^)(void))completion {
417-
// Save off @c completion for when the CAAnimation completes.
418-
self.pendingCompletionBlock = completion;
419-
420-
[CATransaction begin];
433+
// Prepare to move the snackbar.
434+
_snackbarOnscreenConstraint.active = onscreen;
435+
_snackbarOffscreenConstraint.active = !onscreen;
436+
[_containingView setNeedsUpdateConstraints];
421437

422-
// Move the snackbar.
423-
CABasicAnimation *translationAnimation =
424-
[CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
425-
translationAnimation.duration = MDCSnackbarTransitionDuration;
426-
translationAnimation.fromValue = @(fromY);
427-
translationAnimation.toValue = @(toY);
428-
translationAnimation.delegate = self;
429-
translationAnimation.timingFunction =
438+
CAMediaTimingFunction *timingFunction =
430439
[CAMediaTimingFunction mdc_functionWithType:MDCAnimationTimingFunctionEaseInOut];
431-
432-
[snackbarView.layer addAnimation:translationAnimation forKey:@"translation"];
440+
[CATransaction begin];
441+
[CATransaction setAnimationTimingFunction:timingFunction];
442+
443+
// We use UIView animation inside a CATransaction in order to use the custom animation curve.
444+
[UIView animateWithDuration:MDCSnackbarTransitionDuration
445+
delay:0
446+
options:UIViewAnimationOptionCurveEaseInOut
447+
animations:^{
448+
// Trigger snackbar animation.
449+
[_containingView layoutIfNeeded];
450+
} completion:^(BOOL finished) {
451+
if (completion) {
452+
completion();
453+
}
454+
}];
433455

434456
[snackbarView animateContentOpacityFrom:fromContentOpacity
435457
to:toContentOpacity
436-
duration:translationAnimation.duration
437-
timingFunction:translationAnimation.timingFunction];
458+
duration:MDCSnackbarTransitionDuration
459+
timingFunction:timingFunction];
438460
[CATransaction commit];
439461

440462
// Notify the overlay system.
441-
[self notifyOverlayChangeWithFrame:notificationFrame
442-
duration:translationAnimation.duration
463+
[self notifyOverlayChangeWithFrame:[self snackbarRectInScreenCoordinates]
464+
duration:MDCSnackbarTransitionDuration
443465
curve:0
444-
timingFunction:translationAnimation.timingFunction];
466+
timingFunction:timingFunction];
445467
}
446468

447469
- (void)slideInMessageView:(MDCSnackbarMessageView *)snackbarView
@@ -450,11 +472,9 @@ - (void)slideInMessageView:(MDCSnackbarMessageView *)snackbarView
450472
[self triggerSnackbarLayoutChange];
451473

452474
[self slideMessageView:snackbarView
453-
fromY:snackbarView.bounds.size.height + [self staticBottomMargin]
454-
toY:0.0f
475+
onscreen:YES
455476
fromContentOpacity:0
456477
toContentOpacity:1
457-
notificationFrame:[self snackbarRectInScreenCoordinates]
458478
completion:completion];
459479
}
460480

@@ -464,25 +484,12 @@ - (void)slideOutMessageView:(MDCSnackbarMessageView *)snackbarView
464484
[self triggerSnackbarLayoutChange];
465485

466486
[self slideMessageView:snackbarView
467-
fromY:0.0f
468-
toY:snackbarView.bounds.size.height + [self staticBottomMargin]
487+
onscreen:NO
469488
fromContentOpacity:1
470489
toContentOpacity:0
471-
notificationFrame:CGRectNull
472490
completion:completion];
473491
}
474492

475-
#pragma mark - CAAnimationDelegate
476-
477-
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
478-
void (^block)(void) = self.pendingCompletionBlock;
479-
self.pendingCompletionBlock = nil;
480-
481-
if (block) {
482-
block();
483-
}
484-
}
485-
486493
#pragma mark - Keyboard Notifications
487494

488495
- (void)updatesnackbarPositionWithKeyboardUserInfo:(NSDictionary *)userInfo {

0 commit comments

Comments
 (0)