// Copyright 2016-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 "MaterialTextFields.h" @interface TextFieldManualLayoutLegacyExample : UIViewController @property(nonatomic) MDCTextInputControllerLegacyDefault *nameController; @property(nonatomic) MDCTextInputControllerLegacyDefault *phoneController; @end @implementation TextFieldManualLayoutLegacyExample - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; MDCTextField *textFieldName = [[MDCTextField alloc] init]; [self.view addSubview:textFieldName]; textFieldName.delegate = self; textFieldName.clearButtonMode = UITextFieldViewModeUnlessEditing; self.nameController = [[MDCTextInputControllerLegacyDefault alloc] initWithTextInput:textFieldName]; textFieldName.frame = CGRectMake(10, 40, CGRectGetWidth(self.view.bounds) - 20, 0); self.nameController.placeholderText = @"Full Name"; MDCTextField *textFieldPhone = [[MDCTextField alloc] init]; [self.view addSubview:textFieldPhone]; textFieldPhone.delegate = self; textFieldPhone.clearButtonMode = UITextFieldViewModeUnlessEditing; self.phoneController = [[MDCTextInputControllerLegacyDefault alloc] initWithTextInput:textFieldPhone]; textFieldPhone.frame = CGRectMake(10, CGRectGetMaxY(self.nameController.textInput.frame) + 20, CGRectGetWidth(self.view.bounds) - 20, 0); self.phoneController.placeholderText = @"Phone Number"; self.phoneController.helperText = @"XXX-XXX-XXXX"; } - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; [self.nameController.textInput sizeToFit]; [self.phoneController.textInput sizeToFit]; self.phoneController.textInput.frame = CGRectMake( 10, CGRectGetMaxY(self.nameController.textInput.frame) + 20, CGRectGetWidth(self.view.bounds) - 20, CGRectGetHeight(self.phoneController.textInput.frame)); } #pragma mark - UITextFieldDelegate - (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; if (textField == (UITextField *)self.phoneController.textInput && ![self isValidPhoneNumber:textField.text partially:NO]) { [self.phoneController setErrorText:@"Invalid Phone Number" errorAccessibilityValue:nil]; } return NO; } - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { NSString *finishedString = [textField.text stringByReplacingCharactersInRange:range withString:string]; if (textField == (UITextField *)self.nameController.textInput) { if ([finishedString rangeOfCharacterFromSet:[NSCharacterSet decimalDigitCharacterSet]].length && ![self.nameController.errorText isEqualToString:@"You cannot enter numbers"]) { // The entered text contains numbers and we have not set an error [self.nameController setErrorText:@"You cannot enter numbers" errorAccessibilityValue:nil]; // Since we are doing manual layout, we need to react to the expansion of the input that will // come from setting an error. [self.view setNeedsLayout]; } else if (self.nameController.errorText != nil) { // There should be no error but error text is being shown. [self.nameController setErrorText:nil errorAccessibilityValue:nil]; // Since we are doing manual layout, we need to react to the contraction of the input that // will come from setting an error. [self.view setNeedsLayout]; } } if (textField == (UITextField *)self.phoneController.textInput) { if (![self isValidPhoneNumber:finishedString partially:YES] && ![self.phoneController.errorText isEqualToString:@"Invalid phone number"]) { // The entered text is not valid and we have not set an error [self.phoneController setErrorText:@"Invalid phone number" errorAccessibilityValue:nil]; // The text field has helper text that already expanded the frame so we don't need to call // setNeedsLayout. } else if (self.phoneController.errorText != nil) { [self.phoneController setErrorText:nil errorAccessibilityValue:nil]; // The text field has helper text and cannot contract the frame so we don't need to call // setNeedsLayout. } } return YES; } #pragma mark - Phone Number Validation - (BOOL)isValidPhoneNumber:(NSString *)inputString partially:(BOOL)isPartialCheck { // In real life there would be much more robust validation that takes locale into account, checks // against invalid phone numbers (like those that begin with 0), and perhaps even auto-inserts the // hyphens so the user doesn't have to. if (inputString.length == 0) { return YES; } NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789-"]; characterSet = [characterSet invertedSet]; BOOL isValid = ![inputString rangeOfCharacterFromSet:characterSet].length; if (!isPartialCheck) { isValid = isValid && inputString.length == 12; } else { isValid = isValid && inputString.length <= 12; } return isValid; } @end @implementation TextFieldManualLayoutLegacyExample (CatalogByConvention) + (NSDictionary *)catalogMetadata { return @{ @"breadcrumbs" : @[ @"Text Field", @"[Legacy] Manual Layout" ], @"primaryDemo" : @NO, @"presentable" : @NO, }; } @end