blob: 3a86420bcbbaf0e155008fc27d0cdce23ea956cf [file] [log] [blame]
// Copyright 2019-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 "MDCTextControlStyleFilled.h"
#import <Foundation/Foundation.h>
#import "MDCTextControl.h"
#import "MDCTextControlVerticalPositioningReferenceFilled.h"
#include "MaterialAvailability.h"
#import "UIBezierPath+MDCTextControlStyle.h"
static const CGFloat kDefaultFilledStyleTopCornerRadius = (CGFloat)4.0;
@interface MDCTextControlStyleFilled () <CAAnimationDelegate>
@property(strong, nonatomic) CAShapeLayer *filledSublayer;
@property(strong, nonatomic) NSMutableDictionary<NSNumber *, UIColor *> *filledBackgroundColors;
@end
@implementation MDCTextControlStyleFilled
#pragma mark Object Lifecycle
- (instancetype)init {
self = [super init];
if (self) {
[self commonMDCTextControlStyleFilledInit];
}
return self;
}
#pragma mark Setup
- (void)commonMDCTextControlStyleFilledInit {
self.topCornerRadius = kDefaultFilledStyleTopCornerRadius;
[self setUpFilledBackgroundColors];
[self setUpFilledBackgroundSublayer];
}
- (void)setUpFilledBackgroundColors {
self.filledBackgroundColors = [NSMutableDictionary new];
UIColor *filledBackgroundColor = [UIColor secondarySystemBackgroundColor];
self.filledBackgroundColors[@(MDCTextControlStateNormal)] = filledBackgroundColor;
self.filledBackgroundColors[@(MDCTextControlStateEditing)] = filledBackgroundColor;
self.filledBackgroundColors[@(MDCTextControlStateDisabled)] = filledBackgroundColor;
}
- (void)setUpFilledBackgroundSublayer {
self.filledSublayer = [[CAShapeLayer alloc] init];
self.filledSublayer.lineWidth = 0.0;
}
#pragma mark Accessors
- (UIColor *)filledBackgroundColorForState:(MDCTextControlState)state {
return self.filledBackgroundColors[@(state)];
}
- (void)setFilledBackgroundColor:(nonnull UIColor *)filledBackgroundColor
forState:(MDCTextControlState)state {
self.filledBackgroundColors[@(state)] = filledBackgroundColor;
}
#pragma mark MDCTextControl
- (void)applyStyleToTextControl:(UIView<MDCTextControl> *)textControl
animationDuration:(NSTimeInterval)animationDuration {
[super applyStyleToTextControl:textControl animationDuration:animationDuration];
[self applyFilledStyleToView:textControl
state:textControl.textControlState
containerFrame:textControl.containerFrame
animationDuration:animationDuration];
}
- (void)removeStyleFrom:(id<MDCTextControl>)textControl {
[super removeStyleFrom:textControl];
[self.filledSublayer removeFromSuperlayer];
}
- (id<MDCTextControlVerticalPositioningReference>)
positioningReferenceWithFloatingFontLineHeight:(CGFloat)floatingLabelHeight
normalFontLineHeight:(CGFloat)normalFontLineHeight
textRowHeight:(CGFloat)textRowHeight
numberOfTextRows:(CGFloat)numberOfTextRows
density:(CGFloat)density
preferredContainerHeight:(CGFloat)preferredContainerHeight
isMultilineTextControl:(BOOL)isMultilineTextControl {
return [[MDCTextControlVerticalPositioningReferenceFilled alloc]
initWithFloatingFontLineHeight:floatingLabelHeight
normalFontLineHeight:normalFontLineHeight
textRowHeight:textRowHeight
numberOfTextRows:numberOfTextRows
density:density
preferredContainerHeight:preferredContainerHeight
isMultilineTextControl:isMultilineTextControl];
}
- (nonnull MDCTextControlHorizontalPositioningReference *)horizontalPositioningReference {
MDCTextControlHorizontalPositioningReference *positioningReference =
[[MDCTextControlHorizontalPositioningReference alloc] init];
return positioningReference;
}
#pragma mark Custom Styling
- (void)applyFilledStyleToView:(UIView *)view
state:(MDCTextControlState)state
containerFrame:(CGRect)containerFrame
animationDuration:(NSTimeInterval)animationDuration {
self.filledSublayer.fillColor = [self.filledBackgroundColors[@(state)] CGColor];
CGFloat containerHeight = CGRectGetMaxY(containerFrame);
UIBezierPath *filledSublayerBezier =
[self filledSublayerPathWithTextFieldBounds:view.bounds
containerHeight:containerHeight
topCornerRadius:self.topCornerRadius];
self.filledSublayer.path = filledSublayerBezier.CGPath;
if (self.filledSublayer.superlayer != view.layer) {
[view.layer insertSublayer:self.filledSublayer atIndex:0];
}
}
#pragma mark Path Drawing
- (UIBezierPath *)filledSublayerPathWithTextFieldBounds:(CGRect)viewBounds
containerHeight:(CGFloat)containerHeight
topCornerRadius:(CGFloat)topRadius {
UIBezierPath *path = [[UIBezierPath alloc] init];
CGFloat bottomRadius = 0;
CGFloat textFieldWidth = CGRectGetWidth(viewBounds);
CGFloat sublayerMinY = 0;
CGFloat sublayerMaxY = containerHeight;
CGPoint startingPoint = CGPointMake(topRadius, sublayerMinY);
CGPoint topRightCornerPoint1 = CGPointMake(textFieldWidth - topRadius, sublayerMinY);
[path moveToPoint:startingPoint];
[path addLineToPoint:topRightCornerPoint1];
CGPoint topRightCornerPoint2 = CGPointMake(textFieldWidth, sublayerMinY + topRadius);
[path mdc_addTopRightCornerFromPoint:topRightCornerPoint1
toPoint:topRightCornerPoint2
withRadius:topRadius];
CGPoint bottomRightCornerPoint1 = CGPointMake(textFieldWidth, sublayerMaxY - bottomRadius);
CGPoint bottomRightCornerPoint2 = CGPointMake(textFieldWidth - bottomRadius, sublayerMaxY);
[path addLineToPoint:bottomRightCornerPoint1];
[path mdc_addBottomRightCornerFromPoint:bottomRightCornerPoint1
toPoint:bottomRightCornerPoint2
withRadius:bottomRadius];
CGPoint bottomLeftCornerPoint1 = CGPointMake(bottomRadius, sublayerMaxY);
CGPoint bottomLeftCornerPoint2 = CGPointMake(0, sublayerMaxY - bottomRadius);
[path addLineToPoint:bottomLeftCornerPoint1];
[path mdc_addBottomLeftCornerFromPoint:bottomLeftCornerPoint1
toPoint:bottomLeftCornerPoint2
withRadius:bottomRadius];
CGPoint topLeftCornerPoint1 = CGPointMake(0, sublayerMinY + topRadius);
CGPoint topLeftCornerPoint2 = CGPointMake(topRadius, sublayerMinY);
[path addLineToPoint:topLeftCornerPoint1];
[path mdc_addTopLeftCornerFromPoint:topLeftCornerPoint1
toPoint:topLeftCornerPoint2
withRadius:topRadius];
return path;
}
@end