Skip to content

Commit

Permalink
[ReactNative] Fix RCTTextField crash on iOS7
Browse files Browse the repository at this point in the history
Summary:
RCTTextField was its own delegate, that was causing a stall on the main thread,
see more here:
http://stackoverflow.com/questions/19758025/uitextfield-delegate-jumping-to-100-cpu-usage-and-crashing-upon-using-keyboard
  • Loading branch information
tadeuzagallo authored and ide committed Aug 15, 2015
1 parent b10a154 commit e2c1da7
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 36 deletions.
3 changes: 2 additions & 1 deletion Libraries/Text/RCTTextField.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

@class RCTEventDispatcher;

@interface RCTTextField : UITextField<UITextFieldDelegate>
@interface RCTTextField : UITextField

@property (nonatomic, assign) BOOL caretHidden;
@property (nonatomic, assign) BOOL autoCorrect;
Expand All @@ -22,5 +22,6 @@
@property (nonatomic, strong) NSNumber *maxLength;

- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
- (void)textFieldDidChange;

@end
42 changes: 8 additions & 34 deletions Libraries/Text/RCTTextField.m
Original file line number Diff line number Diff line change
Expand Up @@ -27,44 +27,18 @@ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
if ((self = [super initWithFrame:CGRectZero])) {
RCTAssert(eventDispatcher, @"eventDispatcher is a required parameter");
_eventDispatcher = eventDispatcher;
[self addTarget:self action:@selector(_textFieldDidChange) forControlEvents:UIControlEventEditingChanged];
[self addTarget:self action:@selector(_textFieldBeginEditing) forControlEvents:UIControlEventEditingDidBegin];
[self addTarget:self action:@selector(_textFieldEndEditing) forControlEvents:UIControlEventEditingDidEnd];
[self addTarget:self action:@selector(_textFieldSubmitEditing) forControlEvents:UIControlEventEditingDidEndOnExit];
[self addTarget:self action:@selector(textFieldDidChange) forControlEvents:UIControlEventEditingChanged];
[self addTarget:self action:@selector(textFieldBeginEditing) forControlEvents:UIControlEventEditingDidBegin];
[self addTarget:self action:@selector(textFieldEndEditing) forControlEvents:UIControlEventEditingDidEnd];
[self addTarget:self action:@selector(textFieldSubmitEditing) forControlEvents:UIControlEventEditingDidEndOnExit];
_reactSubviews = [[NSMutableArray alloc] init];
self.delegate = self;
}
return self;
}

RCT_NOT_IMPLEMENTED(-initWithFrame:(CGRect)frame)
RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (_maxLength == nil || [string isEqualToString:@"\n"]) { // Make sure forms can be submitted via return
return YES;
}
NSUInteger allowedLength = _maxLength.integerValue - textField.text.length + range.length;
if (string.length > allowedLength) {
if (string.length > 1) {
// Truncate the input string so the result is exactly maxLength
NSString *limitedString = [string substringToIndex:allowedLength];
NSMutableString *newString = textField.text.mutableCopy;
[newString replaceCharactersInRange:range withString:limitedString];
textField.text = newString;
// Collapse selection at end of insert to match normal paste behavior
UITextPosition *insertEnd = [textField positionFromPosition:textField.beginningOfDocument
offset:(range.location + allowedLength)];
textField.selectedTextRange = [textField textRangeFromPosition:insertEnd toPosition:insertEnd];
[self _textFieldDidChange];
}
return NO;
} else {
return YES;
}
}

- (void)setText:(NSString *)text
{
NSInteger eventLag = _nativeEventCount - _mostRecentEventCount;
Expand Down Expand Up @@ -154,7 +128,7 @@ - (BOOL)autoCorrect
return self.autocorrectionType == UITextAutocorrectionTypeYes;
}

- (void)_textFieldDidChange
- (void)textFieldDidChange
{
_nativeEventCount++;
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeChange
Expand All @@ -163,22 +137,22 @@ - (void)_textFieldDidChange
eventCount:_nativeEventCount];
}

- (void)_textFieldEndEditing
- (void)textFieldEndEditing
{
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeEnd
reactTag:self.reactTag
text:self.text
eventCount:_nativeEventCount];
}
- (void)_textFieldSubmitEditing
- (void)textFieldSubmitEditing
{
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeSubmit
reactTag:self.reactTag
text:self.text
eventCount:_nativeEventCount];
}

- (void)_textFieldBeginEditing
- (void)textFieldBeginEditing
{
if (_selectTextOnFocus) {
dispatch_async(dispatch_get_main_queue(), ^{
Expand Down
33 changes: 32 additions & 1 deletion Libraries/Text/RCTTextFieldManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,44 @@
#import "RCTSparseArray.h"
#import "RCTTextField.h"

@interface RCTTextFieldManager() <UITextFieldDelegate>

@end

@implementation RCTTextFieldManager

RCT_EXPORT_MODULE()

- (UIView *)view
{
return [[RCTTextField alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
RCTTextField *textField = [[RCTTextField alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
textField.delegate = self;
return textField;
}

- (BOOL)textField:(RCTTextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField.maxLength == nil || [string isEqualToString:@"\n"]) { // Make sure forms can be submitted via return
return YES;
}
NSUInteger allowedLength = textField.maxLength.integerValue - textField.text.length + range.length;
if (string.length > allowedLength) {
if (string.length > 1) {
// Truncate the input string so the result is exactly maxLength
NSString *limitedString = [string substringToIndex:allowedLength];
NSMutableString *newString = textField.text.mutableCopy;
[newString replaceCharactersInRange:range withString:limitedString];
textField.text = newString;
// Collapse selection at end of insert to match normal paste behavior
UITextPosition *insertEnd = [textField positionFromPosition:textField.beginningOfDocument
offset:(range.location + allowedLength)];
textField.selectedTextRange = [textField textRangeFromPosition:insertEnd toPosition:insertEnd];
[textField textFieldDidChange];
}
return NO;
} else {
return YES;
}
}

RCT_EXPORT_VIEW_PROPERTY(caretHidden, BOOL)
Expand Down

0 comments on commit e2c1da7

Please sign in to comment.