Files
Hippy/ios/sdk/component/textinput/HippyTextField.m
T
luomy 87e2b66a8f Refactor/clang format (#592)
refactor(ios): add clang-format configuration and apply
2021-03-02 19:47:21 +08:00

427 lines
12 KiB
Objective-C

/*!
* iOS SDK
*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company.
* 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 "HippyTextField.h"
#import "HippyConvert.h"
#import "HippyUtils.h"
#import "HippyTextSelection.h"
#import "UIView+Hippy.h"
@implementation HippyUITextField {
BOOL _jsRequestingFirstResponder;
}
- (void)setKeyboardType:(UIKeyboardType)keyboardType {
NSString *tempPwdStr = self.text;
self.text = @"";
if (keyboardType == UIKeyboardTypeTwitter) {
self.secureTextEntry = true;
} else {
self.secureTextEntry = false;
[super setKeyboardType:keyboardType];
}
self.text = tempPwdStr;
}
- (void)setEditable:(BOOL)editable {
_editable = editable;
[self setEnabled:editable];
}
- (void)paste:(id)sender {
_textWasPasted = YES;
[super paste:sender];
}
- (void)hippyWillMakeFirstResponder {
_jsRequestingFirstResponder = YES;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (BOOL)becomeFirstResponder {
if (_responderDelegate && [_responderDelegate respondsToSelector:@selector(textview_becomeFirstResponder)]) {
[_responderDelegate textview_becomeFirstResponder];
}
return [super becomeFirstResponder];
}
- (BOOL)resignFirstResponder {
if (_responderDelegate && [_responderDelegate respondsToSelector:@selector(textview_resignFirstResponder)]) {
[_responderDelegate textview_resignFirstResponder];
}
return [super resignFirstResponder];
}
- (void)textview_becomeFirstResponder {
if (_onFocus) {
_onFocus(@{});
}
}
- (void)textview_resignFirstResponder {
if (_onBlur) {
_onBlur(@{});
}
}
- (void)hippyDidMakeFirstResponder {
_jsRequestingFirstResponder = YES;
}
- (void)didMoveToWindow {
if (_jsRequestingFirstResponder) {
[self becomeFirstResponder];
[self hippyDidMakeFirstResponder];
}
}
- (void)dealloc {
_responderDelegate = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end
@interface HippyTextField () <HippyUITextFieldResponseDelegate>
@end
@implementation HippyTextField {
UITextRange *_previousSelectionRange;
HippyUITextField *_textView;
}
- (void)keyboardWillShow:(NSNotification *)aNotification {
[super keyboardWillShow:aNotification];
NSDictionary *userInfo = [aNotification userInfo];
NSValue *aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardRect = [aValue CGRectValue];
CGFloat keyboardHeight = keyboardRect.size.height;
if (_textView.isFirstResponder && _onKeyboardWillShow) {
_onKeyboardWillShow(@{ @"keyboardHeight": @(keyboardHeight) });
}
}
- (instancetype)init {
if ((self = [super initWithFrame:CGRectZero])) {
[self setContentInset:UIEdgeInsetsZero];
_textView = [[HippyUITextField alloc] initWithFrame:CGRectZero];
_textView.responderDelegate = self;
_textView.backgroundColor = [UIColor clearColor];
_textView.textColor = [UIColor blackColor];
_textView.delegate = self;
[_textView addObserver:self forKeyPath:@"selectedTextRange" options:0 context:nil];
[_textView addTarget:self action:@selector(textFieldDidChange) forControlEvents:UIControlEventEditingChanged];
[_textView addTarget:self action:@selector(textFieldBeginEditing) forControlEvents:UIControlEventEditingDidBegin];
[_textView addTarget:self action:@selector(textFieldSubmitEditing) forControlEvents:UIControlEventEditingDidEndOnExit];
[self addSubview:_textView];
}
return self;
}
- (void)dealloc {
[_textView removeObserver:self forKeyPath:@"selectedTextRange"];
}
HIPPY_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame)
HIPPY_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder)
// This method is overridden for `onKeyPress`. The manager
// will not send a keyPress for text that was pasted.
- (void)paste:(id)sender {
_textWasPasted = YES;
[_textView paste:sender];
}
- (CGRect)textRectForBounds:(CGRect)bounds {
CGRect rect = [_textView textRectForBounds:bounds];
return UIEdgeInsetsInsetRect(rect, self.contentInset);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [_textView textRectForBounds:bounds];
}
- (void)setAutoCorrect:(BOOL)autoCorrect {
_textView.autocorrectionType = (autoCorrect ? UITextAutocorrectionTypeYes : UITextAutocorrectionTypeNo);
}
- (BOOL)autoCorrect {
return _textView.autocorrectionType == UITextAutocorrectionTypeYes;
}
- (void)textFieldDidChange {
UITextRange *selectedRange = [_textView markedTextRange];
NSString *newText = [_textView textInRange:selectedRange];
/**获取中文输入法下高亮部分并直接返回不做_onChangeText */
if (newText.length > 0) {
return;
}
// selectedTextRange observer isn't triggered when you type even though the
// cursor position moves, so we send event again here.
if (!self.hippyTag || !_onChangeText) {
return;
}
NSInteger theMaxLength = self.maxLength.integerValue;
if (theMaxLength == 0) {
theMaxLength = INT_MAX;
}
UITextField *textField = _textView;
NSString *toBeString = textField.text;
NSString *lang = [textField.textInputMode primaryLanguage];
if ([lang isEqualToString:@"zh-Hans"]) // 简体中文输入
{
NSString *toBeString = textField.text;
UITextRange *selectedRange = [textField markedTextRange];
UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0];
if (!position) {
if (toBeString.length > theMaxLength) {
NSRange rangeIndex = [toBeString rangeOfComposedCharacterSequenceAtIndex:theMaxLength];
if (rangeIndex.length == 1) {
textField.text = [toBeString substringToIndex:theMaxLength];
} else {
NSRange rangeRange = [toBeString rangeOfComposedCharacterSequencesForRange:NSMakeRange(0, theMaxLength)];
textField.text = [toBeString substringWithRange:rangeRange];
}
}
}
} else {
if (toBeString.length > theMaxLength) {
NSRange rangeIndex = [toBeString rangeOfComposedCharacterSequenceAtIndex:theMaxLength];
if (rangeIndex.length == 1) {
textField.text = [toBeString substringToIndex:theMaxLength];
} else {
NSRange rangeRange = [toBeString rangeOfComposedCharacterSequencesForRange:NSMakeRange(0, theMaxLength)];
textField.text = [toBeString substringWithRange:rangeRange];
}
}
}
_onChangeText(@{
@"text": _textView.text,
});
[self sendSelectionEvent];
}
- (void)textFieldSubmitEditing {
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (_onEndEditing) {
_onEndEditing(@{
@"text": textField.text,
});
}
if (_onKeyPress) {
_onKeyPress(@{
@"key": @"enter",
});
}
return YES;
}
#pragma mark - Notification Method
- (void)textFieldBeginEditing {
dispatch_async(dispatch_get_main_queue(), ^{
[self sendSelectionEvent];
});
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(__unused HippyTextField *)textField
change:(__unused NSDictionary *)change
context:(__unused void *)context {
if ([keyPath isEqualToString:@"selectedTextRange"]) {
[self sendSelectionEvent];
}
}
// defaultValue
- (void)setDefaultValue:(NSString *)defaultValue {
if (defaultValue && 0 == [self text].length) {
[_textView setText:defaultValue];
}
_defaultValue = defaultValue;
}
// focus/blur
- (void)focus {
[_textView becomeFirstResponder];
}
- (void)blur {
[_textView resignFirstResponder];
}
- (BOOL)becomeFirstResponder {
return [_textView becomeFirstResponder];
}
- (void)textview_becomeFirstResponder {
if (_onFocus) {
_onFocus(@{});
}
}
- (void)textview_resignFirstResponder {
if (_onBlur) {
_onBlur(@{});
}
}
- (BOOL)resignFirstResponder {
[super resignFirstResponder];
return [_textView resignFirstResponder];
}
- (void)updateFrames {
_textView.frame = self.bounds;
}
- (void)layoutSubviews {
[super layoutSubviews];
// Start sending content size updates only after the view has been laid out
// otherwise we send multiple events with bad dimensions on initial render.
// _viewDidCompleteInitialLayout = YES;
[self updateFrames];
}
- (void)setContentInset:(UIEdgeInsets)contentInset {
[super setContentInset:contentInset];
[self updateFrames];
}
- (void)setPlaceholder:(NSString *)placeholder {
_placeholder = placeholder;
[self updatePlaceholder];
}
- (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor {
_placeholderTextColor = placeholderTextColor;
[self updatePlaceholder];
}
- (void)updatePlaceholder {
_textView.placeholder = _placeholder;
if (_placeholderTextColor && _placeholder) {
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:_placeholder
attributes:@{ NSForegroundColorAttributeName: _placeholderTextColor }];
_textView.attributedPlaceholder = attributedString;
}
}
- (void)setText:(NSString *)text {
double version = UIDevice.currentDevice.systemVersion.doubleValue;
if (version >= 10.0 && version < 12.0) {
text = [text stringByReplacingOccurrencesOfString:@"జ్ఞ‌ా" withString:@" "];
}
_textView.text = text;
_text = text;
}
- (void)setFontSize:(NSNumber *)fontSize {
_fontSize = fontSize;
if ([fontSize floatValue] > 0) {
[_textView setFont:[UIFont systemFontOfSize:[fontSize floatValue]]];
}
}
- (void)setValue:(NSString *)value {
[_textView setText:value];
}
- (NSString *)value {
return _textView.text;
}
- (void)setFont:(UIFont *)font {
_textView.font = font;
}
- (void)setTextColor:(UIColor *)textColor {
_textView.textColor = textColor;
}
- (UIColor *)textColor {
return _textView.textColor;
}
- (UIFont *)font {
return _textView.font;
}
- (void)sendSelectionEvent {
if (_onSelectionChange && _textView.selectedTextRange != _previousSelectionRange
&& ![_textView.selectedTextRange isEqual:_previousSelectionRange]) {
_previousSelectionRange = _textView.selectedTextRange;
UITextRange *selection = _textView.selectedTextRange;
NSInteger start = [_textView offsetFromPosition:[_textView beginningOfDocument] toPosition:selection.start];
NSInteger end = [_textView offsetFromPosition:[_textView beginningOfDocument] toPosition:selection.end];
_onSelectionChange(@{
@"selection": @ {
@"start": @(start),
@"end": @(end),
},
});
}
}
- (void)clearText {
[_textView setText:@""];
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (_onKeyPress) {
NSString *resultKey = string;
if ([string isEqualToString:@" "]) {
resultKey = @"space";
} else if ([string isEqualToString:@""]) {
resultKey = @"backspace";
} else if ([string isEqualToString:@"\n"]) {
resultKey = @"enter"; //理论上这里没有enter,不过加一个总没错
}
_onKeyPress(@{ @"key": resultKey });
}
NSString *toBeString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (textField.isSecureTextEntry) {
textField.text = toBeString;
return NO;
}
return YES;
}
@end