Skip to content

Commit

Permalink
[Ink] Retrofit new ink layer with updated animation into existing ink…
Browse files Browse the repository at this point in the history
… API (#2488)

* Add updated ink flag

* Add completion block delegate methods

* Rename existing ink to legacy ink

* Update example with legacy ink as second ink view

* Update comment formatting

* Update formatting

* Update ink unit tests

* Update ink unit tests

* Import UIKit for UIColor support

* Add dependency to Material Math

* Add Material Math dependency

* Use CABasicAnimations where possible

* Add support for maxRippleRadius

* Minor refactoring for timing functions

* Remove unused mark

* Add ink description to comments

* Minor formatting fix

* Nullability updates and clarification comments

* Remove email in comments.

* Add nullability

* Add nullability

* Use year when file was created

* Add float

* Update constant names

* Update formatting

* Update year for legal reasons

* Clarify comment about use of usesCustomInkCenter

* Update cancelAllAnimationsAnimated to stop all active ink ripples

* Remove use of ink layers array and access sublayers directly

* Check layer class in cancelAllAnimationsAnimated

* Use bounds rather than frame in layoutSubviews

* Setting radii in setNeedsLayout
  • Loading branch information
Junius Gunaratne committed Nov 28, 2017
1 parent 0496c22 commit a27d494
Show file tree
Hide file tree
Showing 13 changed files with 1,097 additions and 674 deletions.
1 change: 1 addition & 0 deletions MaterialComponents.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ Pod::Spec.new do |mdc|
spec.ios.deployment_target = '8.0'
spec.public_header_files = "components/#{component.base_name}/src/*.h"
spec.source_files = "components/#{component.base_name}/src/*.{h,m}", "components/#{component.base_name}/src/private/*.{h,m}"
spec.dependency "MaterialComponents/private/Math"
end
component.subspec "ColorThemer" do |spec|
spec.ios.deployment_target = '8.0'
Expand Down
3 changes: 3 additions & 0 deletions components/Ink/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ mdc_public_objc_library(
"QuartzCore",
"UIKit",
],
deps = [
"//components/private/Math",
],
)

mdc_objc_library(
Expand Down
25 changes: 13 additions & 12 deletions components/Ink/examples/InkTypicalUse.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#import <UIKit/UIKit.h>

#import "MaterialInk.h"
#import "MaterialPalettes.h"

#import "supplemental/InkTypicalUseSupplemental.h"

Expand All @@ -31,37 +32,37 @@ @implementation InkTypicalUseViewController
- (void)viewDidLoad {
[super viewDidLoad];

UIColor *blueColor = [MDCPalette.bluePalette.tint500 colorWithAlphaComponent:0.2];
CGFloat spacing = 16;
CGRect customFrame = CGRectMake(0, 0, 200, 200);
CGRect unboundedFrame = CGRectMake(spacing / 2, spacing / 2, customFrame.size.width - spacing,
customFrame.size.height - spacing);
CGRect legacyFrame = CGRectMake(spacing / 2, spacing / 2, CGRectGetWidth(customFrame) - spacing,
CGRectGetHeight(customFrame) - spacing);

// ExampleShapes is a custom UIView with several subviews of various shapes.
self.boundedShapes = [[ExampleShapes alloc] initWithFrame:customFrame];
self.unboundedShape = [[UIView alloc] initWithFrame:unboundedFrame];
self.shapes = [[ExampleShapes alloc] initWithFrame:customFrame];
self.legacyShape = [[UIView alloc] initWithFrame:legacyFrame];

[self setupExampleViews];

_inkTouchControllers = [[NSMutableArray alloc] init];

for (UIView *view in self.boundedShapes.subviews) {
for (UIView *view in self.shapes.subviews) {
MDCInkTouchController *inkTouchController = [[MDCInkTouchController alloc] initWithView:view];
inkTouchController.delegate = self;
inkTouchController.defaultInkView.inkColor = blueColor;
inkTouchController.defaultInkView.usesLegacyInkRipple = NO;
[inkTouchController addInkView];
[_inkTouchControllers addObject:inkTouchController];
}
[self.view addSubview:self.boundedShapes];
[self.view addSubview:self.shapes];

MDCInkTouchController *inkTouchController =
[[MDCInkTouchController alloc] initWithView:self.unboundedShape];
[[MDCInkTouchController alloc] initWithView:self.legacyShape];
inkTouchController.delegate = self;
[inkTouchController addInkView];

UIColor *blueColor = [UIColor colorWithRed:11/255.0 green:232/255.0 blue:94/255.0 alpha:0.2];
inkTouchController.defaultInkView.inkColor = blueColor;
inkTouchController.defaultInkView.inkStyle = MDCInkStyleUnbounded;
[inkTouchController addInkView];
[_inkTouchControllers addObject:inkTouchController];
[self.view addSubview:self.unboundedShape];
[self.view addSubview:self.legacyShape];
}

#pragma mark - Private
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@

@interface InkTypicalUseViewController : UIViewController

@property(nonatomic, strong) ExampleShapes *boundedShapes;
@property(nonatomic, strong) UIView *unboundedShape;
@property(nonatomic, strong) ExampleShapes *shapes;
@property(nonatomic, strong) UIView *legacyShape;

@end

Expand Down
48 changes: 25 additions & 23 deletions components/Ink/examples/supplemental/InkTypicalUseSupplemental.m
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ - (id)initWithFrame:(CGRect)frame {
CGFloat padding = 8;
CGFloat bigViewFrameHeight = 130;
CGRect bigViewFrame =
CGRectMake(padding, padding, frame.size.width - 2 * padding, bigViewFrameHeight);
CGRectMake(padding, padding, CGRectGetWidth(frame) - 2 * padding, bigViewFrameHeight);
UIView *bigView = [[UIView alloc] initWithFrame:bigViewFrame];
bigView.backgroundColor = [UIColor whiteColor];
[self addSubview:bigView];
Expand All @@ -54,7 +54,7 @@ - (id)initWithFrame:(CGRect)frame {
[self addSubview:pseudoButtonView];

CGFloat pseudoFABViewFrameLeft =
padding + frame.size.width - 2 * padding - buttonViewDim + padding - fabPadding * 2;
padding + CGRectGetWidth(frame) - 2 * padding - buttonViewDim + padding - fabPadding * 2;
CGRect pseudoFABViewFrame =
CGRectMake(pseudoFABViewFrameLeft, padding + bigViewFrameHeight + padding,
buttonViewDim + fabPadding, buttonViewDim + fabPadding);
Expand Down Expand Up @@ -98,43 +98,45 @@ - (void)setupExampleViews {
self.view.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1];

CGRect boundedTitleLabelFrame =
CGRectMake(0, self.boundedShapes.frame.size.height, self.boundedShapes.frame.size.width, 24);
CGRectMake(0, CGRectGetHeight(self.shapes.frame), CGRectGetWidth(self.shapes.frame), 24);
UILabel *boundedTitleLabel = [[UILabel alloc] initWithFrame:boundedTitleLabelFrame];
boundedTitleLabel.text = @"Bounded";
boundedTitleLabel.text = @"Ink";
boundedTitleLabel.textAlignment = NSTextAlignmentCenter;
boundedTitleLabel.font = [MDCTypography captionFont];
boundedTitleLabel.alpha = [MDCTypography captionFontOpacity];
[self.boundedShapes addSubview:boundedTitleLabel];
[self.shapes addSubview:boundedTitleLabel];

self.unboundedShape.autoresizingMask =
self.legacyShape.autoresizingMask =
UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin;
self.unboundedShape.backgroundColor = [UIColor whiteColor];

CGRect unboundedTitleLabelFrame = CGRectMake(0, self.unboundedShape.frame.size.height,
self.unboundedShape.frame.size.width, 36);
UILabel *unboundedTitleLabel = [[UILabel alloc] initWithFrame:unboundedTitleLabelFrame];
unboundedTitleLabel.text = @"Unbounded";
unboundedTitleLabel.textAlignment = NSTextAlignmentCenter;
unboundedTitleLabel.font = [MDCTypography captionFont];
unboundedTitleLabel.alpha = [MDCTypography captionFontOpacity];
[self.unboundedShape addSubview:unboundedTitleLabel];
self.legacyShape.backgroundColor = [UIColor whiteColor];

CGRect legacyTitleLabelFrame = CGRectMake(0,
CGRectGetHeight(self.legacyShape.frame),
CGRectGetWidth(self.legacyShape.frame),
36);
UILabel *legacyTitleLabel = [[UILabel alloc] initWithFrame:legacyTitleLabelFrame];
legacyTitleLabel.text = @"Legacy Ink";
legacyTitleLabel.textAlignment = NSTextAlignmentCenter;
legacyTitleLabel.font = [MDCTypography captionFont];
legacyTitleLabel.alpha = [MDCTypography captionFontOpacity];
[self.legacyShape addSubview:legacyTitleLabel];
}

- (void)viewWillLayoutSubviews {
CGFloat offset = 8;
CGFloat shapeDimension = 200;
CGFloat spacing = 16;
if (self.view.frame.size.height > self.view.frame.size.width) {
self.boundedShapes.center =
if (CGRectGetHeight(self.view.frame) > CGRectGetWidth(self.view.frame)) {
self.shapes.center =
CGPointMake(self.view.center.x, self.view.center.y - shapeDimension - offset);
self.unboundedShape.center =
self.legacyShape.center =
CGPointMake(self.view.center.x, self.view.center.y + spacing * 2 + offset);
} else {
self.boundedShapes.center = CGPointMake(self.view.center.x - shapeDimension / 2 - spacing * 2,
self.view.center.y / 2 + spacing * 2);
self.unboundedShape.center = CGPointMake(self.view.center.x + shapeDimension / 2 + spacing * 2,
self.view.center.y / 2 + spacing * 2);
self.shapes.center = CGPointMake(self.view.center.x - shapeDimension / 2 - spacing * 2,
self.view.center.y / 2 + spacing * 2);
self.legacyShape.center = CGPointMake(self.view.center.x + shapeDimension / 2 + spacing * 2,
self.view.center.y / 2 + spacing * 2);
}
}

Expand Down
13 changes: 11 additions & 2 deletions components/Ink/src/MDCInkView.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,27 +54,36 @@ typedef NS_ENUM(NSInteger, MDCInkStyle) {
@property(nonatomic, assign) MDCInkStyle inkStyle;

/** The foreground color of the ink. The default value is defaultInkColor. */
@property(nonatomic, strong, null_resettable) UIColor *inkColor;
@property(nonatomic, strong, nonnull) UIColor *inkColor UI_APPEARANCE_SELECTOR;

/** Default color used for ink if no color is specified. */
@property(nonatomic, strong, readonly, nonnull) UIColor *defaultInkColor;

/**
Maximum radius of the ink. If the radius <= 0 then half the length of the diagonal of self.bounds
is used. This value is ignored if @c inkStyle is set to |MDCInkStyleBounded|.
Ignored if updated ink is used.
*/
@property(nonatomic, assign) CGFloat maxRippleRadius;

/**
Use the older legacy version of the ink ripple. Default is YES.
*/
@property(nonatomic, assign) BOOL usesLegacyInkRipple;

/**
Use a custom center for the ink splash. If YES, then customInkCenter is used, otherwise the
center of self.bounds is used. Default is NO.
Affects behavior only if usesLegacyInkRipple is enabled.
*/
@property(nonatomic, assign) BOOL usesCustomInkCenter;

/**
Custom center for the ink splash in the view’s coordinate system.
Ignored if usesCustomInkCenter is not set.
Affects behavior only if both usesCustomInkCenter and usesLegacyInkRipple are enabled.
*/
@property(nonatomic, assign) CGPoint customInkCenter;

Expand Down
97 changes: 86 additions & 11 deletions components/Ink/src/MDCInkView.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,23 @@
#import "MDCInkView.h"

#import "private/MDCInkLayer.h"
#import "private/MDCLegacyInkLayer.h"

@interface MDCInkView () <MDCInkLayerDelegate>

@property(nonatomic, copy) MDCInkCompletionBlock startInkRippleCompletionBlock;
@property(nonatomic, copy) MDCInkCompletionBlock endInkRippleCompletionBlock;
@property(nonatomic, strong) MDCInkLayer *activeInkLayer;

// Legacy ink ripple
@property(nonatomic, readonly) MDCLegacyInkLayer *inkLayer;

@interface MDCInkView ()
@property(nonatomic, readonly) MDCInkLayer *inkLayer;
@end

@implementation MDCInkView

+ (Class)layerClass {
return [MDCInkLayer class];
return [MDCLegacyInkLayer class];
}

- (instancetype)initWithFrame:(CGRect)frame {
Expand All @@ -49,6 +57,20 @@ - (void)commonMDCInkViewInit {
self.backgroundColor = [UIColor clearColor];
self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
self.inkColor = self.defaultInkColor;
_usesLegacyInkRipple = YES;
}

- (void)layoutSubviews {
[super layoutSubviews];
CGRect inkBounds = CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds));

// When bounds change ensure all ink layer bounds are changed too.
for (CALayer *layer in self.layer.sublayers) {
if ([layer isKindOfClass:[MDCInkLayer class]]) {
MDCInkLayer *inkLayer = (MDCInkLayer *)layer;
inkLayer.bounds = inkBounds;
}
}
}

- (void)setInkStyle:(MDCInkStyle)inkStyle {
Expand Down Expand Up @@ -103,30 +125,69 @@ - (void)setCustomInkCenter:(CGPoint)customInkCenter {
self.inkLayer.customInkCenter = customInkCenter;
}

- (MDCInkLayer *)inkLayer {
return (MDCInkLayer *)self.layer;
- (MDCLegacyInkLayer *)inkLayer {
return (MDCLegacyInkLayer *)self.layer;
}

- (void)startTouchBeganAnimationAtPoint:(CGPoint)point
completion:(MDCInkCompletionBlock)completionBlock {
[self.inkLayer spreadFromPoint:point completion:completionBlock];
if (self.usesLegacyInkRipple) {
[self.inkLayer spreadFromPoint:point completion:completionBlock];
} else {
self.startInkRippleCompletionBlock = completionBlock;
MDCInkLayer *inkLayer = [MDCInkLayer layer];
inkLayer.inkColor = self.inkColor;
inkLayer.maxRippleRadius = self.maxRippleRadius;
inkLayer.animationDelegate = self;

switch (self.inkStyle) {
case MDCInkStyleBounded:
self.clipsToBounds = YES;
break;
case MDCInkStyleUnbounded:
self.clipsToBounds = NO;
break;
}

inkLayer.opacity = 0;
inkLayer.frame = self.bounds;
[self.layer addSublayer:inkLayer];
[inkLayer startAnimationAtPoint:point];
self.activeInkLayer = inkLayer;
}
}

- (void)startTouchEndedAnimationAtPoint:(__unused CGPoint)point
- (void)startTouchEndedAnimationAtPoint:(CGPoint)point
completion:(MDCInkCompletionBlock)completionBlock {
[self.inkLayer evaporateWithCompletion:completionBlock];
if (self.usesLegacyInkRipple) {
[self.inkLayer evaporateWithCompletion:completionBlock];
} else {
self.endInkRippleCompletionBlock = completionBlock;
[self.activeInkLayer endAnimationAtPoint:point];
}
}

- (void)cancelAllAnimationsAnimated:(BOOL)animated {
[self.inkLayer resetAllInk:animated];
if (self.usesLegacyInkRipple) {
[self.inkLayer resetAllInk:animated];
} else {
for (CALayer *layer in self.layer.sublayers) {
if ([layer isKindOfClass:[MDCInkLayer class]]) {
MDCInkLayer *inkLayer = (MDCInkLayer *)layer;
if (animated) {
[inkLayer endAnimationAtPoint:CGPointZero];
} else {
[inkLayer removeFromSuperlayer];
}
}
}
}
}

- (UIColor *)defaultInkColor {
return [[UIColor alloc] initWithWhite:0 alpha:0.06f];
}

#pragma mark

+ (MDCInkView *)injectedInkViewForView:(UIView *)view {
MDCInkView *foundInkView = nil;
for (MDCInkView *subview in view.subviews) {
Expand All @@ -144,4 +205,18 @@ + (MDCInkView *)injectedInkViewForView:(UIView *)view {
return foundInkView;
}

#pragma mark - MDCInkLayerDelegate

- (void)inkLayerAnimationDidStart:(MDCInkLayer *)inkLayer {
if (self.activeInkLayer == inkLayer && self.startInkRippleCompletionBlock) {
self.startInkRippleCompletionBlock();
}
}

- (void)inkLayerAnimationDidEnd:(MDCInkLayer *)inkLayer {
if (self.activeInkLayer == inkLayer && self.endInkRippleCompletionBlock) {
self.endInkRippleCompletionBlock();
}
}

@end
Loading

0 comments on commit a27d494

Please sign in to comment.