mirror of
https://github.com/material-components/material-components-ios.git
synced 2026-01-21 20:44:00 +08:00
### Context A client is wanting to animate the corner radius of a view that has a `MDCShadowLayer` backing layer. This currently doesn't render correctly. The work done in #5398 still does not support animating the corner radius. #5398 additionally doesn't animate the `shadowPath` property so it still won't work properly. We set the properties in the `CATransaction completionBlock` because this prevents the layer from snapping back to it's original state after the animation completes. If a client desires to use a `CATransaction` wrapper around this animation they can do so as outlined [here](https://developer.apple.com/documentation/quartzcore/catransaction). ### The problem Animating corner radius changes in a CAAnimation don't render ### The fix Add an API to allow clients to animate corner radius changes for MDCShadowLayer. ### Alternatives 1. Alternatives to adding a new API would be to have the a client use our private API which isn't desirable and not future proof as discussed in #5566. 2. Add an API `- (CAGroupAnimation *)animateCornerRadius:(CGFloat)cornerRadius;` that would return all 5 of the animations and clients could set the duration and timingFunction themselves but this doesn't allow for the layers to stay animated after completion. If we want the layer to be animated after completion we could use `kCAFillModeForwards` and `removeOnCompletion = NO` but that would prevent use from animating back to the original state. ### Videos | Before | After | | - | - | ||| | Alternative 2 | Without corner radius | | - | - | ||| ### Bugs remaining 1. Set the MDShadowLayer cornerRadius to 0. 2. Call the new API with a duration of 5 seconds and a cornerRadius of 10. 3. Before that animation completes call the API again with a cornerRadius of 0. #### Expected behavior The first animation would complete and then the second animation would start. #### Actual behavior The animation jumps to 10 and then animates back to 0. **_note_** This could be avoided if we added a property `BOOL` called `_isAnimating` and if that is set to `YES` then we would stash the next animation. This isn't added because most animations will be too fast for a user to tap that fast. ### Tested cl/219964460
108 lines
3.4 KiB
Objective-C
108 lines
3.4 KiB
Objective-C
// Copyright 2018-present the Material Components for iOS authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#import "ShadowCornerRadiusAnimationViewController.h"
|
|
|
|
#import "MaterialAnimationTiming.h"
|
|
#import "MaterialButtons+ButtonThemer.h"
|
|
#import "MaterialButtons.h"
|
|
#import "MaterialShadowLayer.h"
|
|
|
|
static const CGFloat kStartCornerRadius = (CGFloat)0.001;
|
|
static const CGFloat kEndCornerRadius = (CGFloat)25.0;
|
|
static const CGFloat kAnimationDuration = (CGFloat)2.5;
|
|
|
|
@interface CustomView : UIView
|
|
|
|
@end
|
|
|
|
@implementation CustomView
|
|
|
|
+ (Class)layerClass {
|
|
return [MDCShadowLayer class];
|
|
}
|
|
|
|
- (MDCShadowLayer *)shadowLayer {
|
|
return (MDCShadowLayer *)self.layer;
|
|
}
|
|
|
|
- (void)setElevation:(CGFloat)points {
|
|
[(MDCShadowLayer *)self.layer setElevation:points];
|
|
}
|
|
|
|
@end
|
|
|
|
@interface ShadowCornerRadiusAnimationViewController ()
|
|
@property(nonatomic, strong, nullable) MDCButton *button;
|
|
@property(nonatomic, strong, nullable) CustomView *customView;
|
|
@end
|
|
|
|
@implementation ShadowCornerRadiusAnimationViewController {
|
|
BOOL _animated;
|
|
}
|
|
|
|
- (void)viewDidLoad {
|
|
[super viewDidLoad];
|
|
|
|
_animated = NO;
|
|
self.view.backgroundColor = UIColor.whiteColor;
|
|
self.button = [[MDCButton alloc] init];
|
|
[self.button setTitle:@"Animation View" forState:UIControlStateNormal];
|
|
[MDCContainedButtonThemer applyScheme:[[MDCButtonScheme alloc] init] toButton:self.button];
|
|
[self.button sizeToFit];
|
|
[self.button addTarget:self
|
|
action:@selector(animateView)
|
|
forControlEvents:UIControlEventTouchUpInside];
|
|
[self.view addSubview:self.button];
|
|
|
|
self.customView = [[CustomView alloc] initWithFrame:CGRectZero];
|
|
self.customView.backgroundColor = UIColor.lightGrayColor;
|
|
[self.customView setElevation:(CGFloat)8.0];
|
|
[self.view addSubview:self.customView];
|
|
}
|
|
|
|
- (void)viewWillLayoutSubviews {
|
|
[super viewWillLayoutSubviews];
|
|
|
|
self.button.center = CGPointMake(self.view.center.x, self.view.center.y - 100);
|
|
self.customView.bounds = CGRectMake(0, 0, 100, 100);
|
|
self.customView.center = CGPointMake(self.view.center.x, self.view.center.y + 20);
|
|
}
|
|
|
|
- (void)animateView {
|
|
CAMediaTimingFunction *timingFunction =
|
|
[CAMediaTimingFunction mdc_functionWithType:MDCAnimationTimingFunctionEaseInOut];
|
|
if (!_animated) {
|
|
[self.customView.shadowLayer animateCornerRadius:kEndCornerRadius
|
|
withTimingFunction:timingFunction
|
|
duration:kAnimationDuration];
|
|
} else {
|
|
[self.customView.shadowLayer animateCornerRadius:kStartCornerRadius
|
|
withTimingFunction:timingFunction
|
|
duration:kAnimationDuration];
|
|
}
|
|
_animated = !_animated;
|
|
}
|
|
|
|
+ (NSDictionary *)catalogMetadata {
|
|
return @{
|
|
@"breadcrumbs" : @[ @"Shadow", @"Shadow Corner Animation" ],
|
|
@"description" : @"Animate shadows within a CABasicAnimation.",
|
|
@"primaryDemo" : @NO,
|
|
@"presentable" : @NO,
|
|
};
|
|
}
|
|
|
|
@end
|