blob: 1fd583505905e810349d860938e695d46a521f7f [file] [log] [blame]
// Copyright 2017-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 "MDCTextInputControllerOutlinedTextArea.h"
#import "MDCTextInput.h"
#import "MDCTextInputBorderView.h"
#import "MDCTextInputController.h"
#import "MDCTextInputControllerBase.h"
#import "MDCTextInputControllerFloatingPlaceholder.h"
#import "MDCTextInputUnderlineView.h"
#import "private/MDCTextInputControllerBase+Subclassing.h"
#import "MaterialMath.h"
/**
Note: Right now this is a subclass of MDCTextInputControllerBase since they share a vast
majority of code. If the designs diverge further, this would make a good candidate for its own
class.
*/
#pragma mark - Constants
static const CGFloat MDCTextInputTextFieldOutlinedTextAreaFullPadding = 16;
static const CGFloat MDCTextInputTextFieldOutlinedTextAreaHalfPadding = 8;
// The guidelines have 8 points of padding but since the fonts on iOS are slightly smaller, we need
// to add points to keep the versions at the same height.
static const CGFloat MDCTextInputTextFieldOutlinedTextAreaPaddingAdjustment = 1;
#pragma mark - Class Properties
static UIRectCorner _roundedCornersDefault = UIRectCornerAllCorners;
@interface MDCTextInputControllerOutlinedTextArea ()
@property(nonatomic, strong) NSLayoutConstraint *placeholderTop;
@end
@implementation MDCTextInputControllerOutlinedTextArea
- (instancetype)initWithTextInput:(UIView<MDCTextInput> *)input {
NSAssert([input conformsToProtocol:@protocol(MDCMultilineTextInput)],
@"This design is meant for multi-line text fields only.");
self = [super initWithTextInput:input];
if (self) {
}
return self;
}
#pragma mark - Properties Implementations
- (BOOL)isFloatingEnabled {
return YES;
}
- (void)setFloatingEnabled:(__unused BOOL)floatingEnabled {
// Unused. Floating is always enabled.
}
+ (UIRectCorner)roundedCornersDefault {
return _roundedCornersDefault;
}
+ (void)setRoundedCornersDefault:(UIRectCorner)roundedCornersDefault {
_roundedCornersDefault = roundedCornersDefault;
}
#pragma mark - MDCTextInputPositioningDelegate
// clang-format off
/**
textInsets: is the source of truth for vertical layout. It's used to figure out the proper
height and also where to place the placeholder / text field.
NOTE: It's applied before the textRect is flipped for RTL. So all calculations are done here à la
LTR.
The vertical layout is, at most complex, this form:
MDCTextInputTextFieldOutlinedTextAreaHalfPadding + // Small padding
MDCTextInputTextFieldOutlinedTextAreaPaddingAdjustment // Additional point (iOS specific)
placeholderEstimatedHeight // Height of placeholder
MDCTextInputTextFieldOutlinedTextAreaHalfPadding + // Small padding
MDCTextInputTextFieldOutlinedTextAreaPaddingAdjustment // Additional point (iOS specific)
MDCCeil(MAX(self.textInput.font.lineHeight, // Text field or placeholder
self.textInput.placeholderLabel.font.lineHeight))
underlineOffset // Small Padding +
// underlineLabelsOffset From super class.
*/
// clang-format on
- (UIEdgeInsets)textInsets:(UIEdgeInsets)defaultInsets {
UIEdgeInsets textInsets = [super textInsets:defaultInsets];
textInsets.top = MDCTextInputTextFieldOutlinedTextAreaHalfPadding +
MDCTextInputTextFieldOutlinedTextAreaPaddingAdjustment +
MDCRint(self.textInput.placeholderLabel.font.lineHeight *
(CGFloat)self.floatingPlaceholderScale.floatValue) +
MDCTextInputTextFieldOutlinedTextAreaHalfPadding +
MDCTextInputTextFieldOutlinedTextAreaPaddingAdjustment;
// .bottom = underlineOffset + the half padding above the line but below the text field and any
// space needed for the labels and / or line.
// Legacy has an additional half padding here but this version does not.
CGFloat underlineOffset = [self underlineOffset];
textInsets.bottom = underlineOffset;
textInsets.left = MDCTextInputTextFieldOutlinedTextAreaFullPadding;
textInsets.right = MDCTextInputTextFieldOutlinedTextAreaFullPadding;
return textInsets;
}
#pragma mark - Layout
- (void)updateBorder {
[super updateBorder];
UIColor *borderColor = self.textInput.isEditing ? self.activeColor : self.normalColor;
self.textInput.borderView.borderStrokeColor =
(self.isDisplayingCharacterCountError || self.isDisplayingErrorText) ? self.errorColor
: borderColor;
self.textInput.borderView.borderPath.lineWidth = self.textInput.isEditing ? 2 : 1;
}
- (void)updateLayout {
[super updateLayout];
if (!self.textInput) {
return;
}
NSAssert([self.textInput conformsToProtocol:@protocol(MDCMultilineTextInput)],
@"This design is meant for multi-line text fields only.");
if (![self.textInput conformsToProtocol:@protocol(MDCMultilineTextInput)]) {
return;
}
((UIView<MDCMultilineTextInput> *)self.textInput).expandsOnOverflow = NO;
((UIView<MDCMultilineTextInput> *)self.textInput).minimumLines = 5;
self.textInput.underline.alpha = 0;
if (!self.placeholderTop) {
self.placeholderTop =
[NSLayoutConstraint constraintWithItem:self.textInput.placeholderLabel
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.textInput
attribute:NSLayoutAttributeTop
multiplier:1
constant:MDCTextInputTextFieldOutlinedTextAreaFullPadding];
self.placeholderTop.priority = UILayoutPriorityDefaultHigh;
self.placeholderTop.active = YES;
}
}
// The measurement from bottom to underline center Y.
- (CGFloat)underlineOffset {
// The amount of space underneath the underline depends on whether there is content in the
// underline labels.
CGFloat underlineLabelsOffset = 0;
CGFloat scale = UIScreen.mainScreen.scale;
if (self.textInput.leadingUnderlineLabel.text.length) {
underlineLabelsOffset =
MDCCeil(self.textInput.leadingUnderlineLabel.font.lineHeight * scale) / scale;
}
if (self.textInput.trailingUnderlineLabel.text.length || self.characterCountMax) {
underlineLabelsOffset =
MAX(underlineLabelsOffset,
MDCCeil(self.textInput.trailingUnderlineLabel.font.lineHeight * scale) / scale);
}
CGFloat underlineOffset = underlineLabelsOffset;
underlineOffset += MDCTextInputTextFieldOutlinedTextAreaHalfPadding;
if (!MDCCGFloatEqual(underlineLabelsOffset, 0)) {
underlineOffset += MDCTextInputTextFieldOutlinedTextAreaHalfPadding;
}
return underlineOffset;
}
@end