| // 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 |