Skip to content

Commit ee9697e

Browse files
sherginfacebook-github-bot
authored andcommitted
Introducing RCTBackedTextInputDelegate
Summary: Nothing behavioral changed in this diff; just moving code around. `RCTBackedTextInputDelegate` is the new protocol which supposed to be common determinator among of UITextFieldDelegate and UITextViewDelegate (and bunch of events and notifications around UITextInput and UITextView). We need this reach two goals in the future: * Incapsulate UIKit imperfections related hack in dedicated protocol adapter. So, doing this we can fix more UIKit related bugs without touching real RN text handling logic. (Yes, we still have a bunch of bugs, which we cannot fix because it is undoable with the current architecture. This diff does NOT fix anything though.) * We can unify logic in RCTTextField and RCTTextView (even more!), moving it to a superclass. If we do so, we can fix another bunch of bugs related to RN imperfections. And have singleline/multiline inputs implementations even more consistent. Reviewed By: mmmulani Differential Revision: D5296041 fbshipit-source-id: 318fd850e946a3c34933002a6bde34a0a45a6293
1 parent 2a7bde0 commit ee9697e

15 files changed

+460
-209
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
#import <UIKit/UIKit.h>
11+
12+
@protocol RCTBackedTextInputViewProtocol;
13+
14+
@protocol RCTBackedTextInputDelegate <NSObject>
15+
16+
- (BOOL)textInputShouldBeginEditing; // Return `NO` to disallow editing.
17+
- (void)textInputDidBeginEditing;
18+
19+
- (BOOL)textInputShouldEndEditing; // Return `YES` to allow editing to stop and to resign first responder status. `NO` to disallow the editing session to end.
20+
- (void)textInputDidEndEditing; // May be called if forced even if `textInputShouldEndEditing` returns `NO` (e.g. view removed from window) or `[textInput endEditing:YES]` called.
21+
- (void)textInputDidEndEditingOnExit; // May be called right before `textInputShouldEndEditing` if "Submit" button was pressed.
22+
23+
- (BOOL)textInputShouldChangeTextInRange:(NSRange)range replacementText:(NSString *)string; // Return NO to not change text.
24+
- (void)textInputDidChange;
25+
26+
- (void)textInputDidChangeSelection;
27+
28+
@end
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
#import <UIKit/UIKit.h>
11+
12+
#import "RCTBackedTextInputViewProtocol.h"
13+
#import "RCTBackedTextInputDelegate.h"
14+
15+
#pragma mark - RCTBackedTextFieldDelegateAdapter (for UITextField)
16+
17+
@interface RCTBackedTextFieldDelegateAdapter : NSObject
18+
19+
- (instancetype)initWithTextField:(UITextField<RCTBackedTextInputViewProtocol> *)backedTextInput;
20+
21+
@end
22+
23+
#pragma mark - RCTBackedTextViewDelegateAdapter (for UITextView)
24+
25+
@interface RCTBackedTextViewDelegateAdapter : NSObject
26+
27+
- (instancetype)initWithTextView:(UITextView<RCTBackedTextInputViewProtocol> *)backedTextInput;
28+
29+
@end
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
#import "RCTBackedTextInputDelegateAdapter.h"
11+
12+
#pragma mark - RCTBackedTextFieldDelegateAdapter (for UITextField)
13+
14+
static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingContext;
15+
16+
@interface RCTBackedTextFieldDelegateAdapter () <UITextFieldDelegate>
17+
@end
18+
19+
@implementation RCTBackedTextFieldDelegateAdapter {
20+
__weak UITextField<RCTBackedTextInputViewProtocol> *_backedTextInput;
21+
__unsafe_unretained UITextField<RCTBackedTextInputViewProtocol> *_unsafeBackedTextInput;
22+
}
23+
24+
- (instancetype)initWithTextField:(UITextField<RCTBackedTextInputViewProtocol> *)backedTextInput
25+
{
26+
if (self = [super init]) {
27+
_backedTextInput = backedTextInput;
28+
_unsafeBackedTextInput = backedTextInput;
29+
backedTextInput.delegate = self;
30+
31+
[_backedTextInput addTarget:self action:@selector(textFieldDidChange) forControlEvents:UIControlEventEditingChanged];
32+
[_backedTextInput addTarget:self action:@selector(textFieldDidEndEditingOnExit) forControlEvents:UIControlEventEditingDidEndOnExit];
33+
34+
// We have to use `unsafe_unretained` pointer to `backedTextInput` for subscribing (and especially unsubscribing) for it
35+
// because `weak` pointers do not KVO complient, unfortunately.
36+
[_unsafeBackedTextInput addObserver:self forKeyPath:@"selectedTextRange" options:0 context:TextFieldSelectionObservingContext];
37+
}
38+
39+
return self;
40+
}
41+
42+
- (void)dealloc
43+
{
44+
[_backedTextInput removeTarget:self action:nil forControlEvents:UIControlEventEditingChanged];
45+
[_backedTextInput removeTarget:self action:nil forControlEvents:UIControlEventEditingDidEndOnExit];
46+
[_unsafeBackedTextInput removeObserver:self forKeyPath:@"selectedTextRange" context:TextFieldSelectionObservingContext];
47+
}
48+
49+
#pragma mark - UITextFieldDelegate
50+
51+
- (BOOL)textFieldShouldBeginEditing:(__unused UITextField *)textField
52+
{
53+
return [_backedTextInput.textInputDelegate textInputShouldBeginEditing];
54+
}
55+
56+
- (void)textFieldDidBeginEditing:(__unused UITextField *)textField
57+
{
58+
[_backedTextInput.textInputDelegate textInputDidBeginEditing];
59+
}
60+
61+
- (BOOL)textFieldShouldEndEditing:(__unused UITextField *)textField
62+
{
63+
return [_backedTextInput.textInputDelegate textInputShouldEndEditing];
64+
}
65+
66+
- (void)textFieldDidEndEditing:(__unused UITextField *)textField
67+
{
68+
[_backedTextInput.textInputDelegate textInputDidEndEditing];
69+
}
70+
71+
- (BOOL)textField:(__unused UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
72+
{
73+
return [_backedTextInput.textInputDelegate textInputShouldChangeTextInRange:range replacementText:string];
74+
}
75+
76+
#pragma mark - Key Value Observing
77+
78+
- (void)observeValueForKeyPath:(NSString *)keyPath
79+
ofObject:(nullable id)object
80+
change:(NSDictionary *)change
81+
context:(void *)context
82+
{
83+
if (context == TextFieldSelectionObservingContext) {
84+
if ([keyPath isEqualToString:@"selectedTextRange"]) {
85+
[_backedTextInput.textInputDelegate textInputDidChangeSelection];
86+
}
87+
88+
return;
89+
}
90+
91+
[super observeValueForKeyPath:keyPath
92+
ofObject:object
93+
change:change
94+
context:context];
95+
}
96+
97+
#pragma mark - UIControlEventEditing* Family Events
98+
99+
- (void)textFieldDidChange
100+
{
101+
[_backedTextInput.textInputDelegate textInputDidChange];
102+
}
103+
104+
- (void)textFieldDidEndEditingOnExit
105+
{
106+
[_backedTextInput.textInputDelegate textInputDidEndEditingOnExit];
107+
}
108+
109+
#pragma mark - UIKeyboardInput (private UIKit protocol)
110+
111+
// This method allows us to detect a [Backspace] `keyPress`
112+
// even when there is no more text in the `UITextField`.
113+
- (BOOL)keyboardInputShouldDelete:(__unused UITextField *)textField
114+
{
115+
[_backedTextInput.textInputDelegate textInputShouldChangeTextInRange:NSMakeRange(0, 0) replacementText:@""];
116+
return YES;
117+
}
118+
119+
@end
120+
121+
#pragma mark - RCTBackedTextViewDelegateAdapter (for UITextView)
122+
123+
@interface RCTBackedTextViewDelegateAdapter () <UITextViewDelegate>
124+
@end
125+
126+
@implementation RCTBackedTextViewDelegateAdapter {
127+
__weak UITextView<RCTBackedTextInputViewProtocol> *_backedTextInput;
128+
}
129+
130+
- (instancetype)initWithTextView:(UITextView<RCTBackedTextInputViewProtocol> *)backedTextInput
131+
{
132+
if (self = [super init]) {
133+
_backedTextInput = backedTextInput;
134+
backedTextInput.delegate = self;
135+
}
136+
137+
return self;
138+
}
139+
140+
#pragma mark - UITextViewDelegate
141+
142+
- (BOOL)textViewShouldBeginEditing:(__unused UITextView *)textView
143+
{
144+
return [_backedTextInput.textInputDelegate textInputShouldBeginEditing];
145+
}
146+
147+
- (void)textViewDidBeginEditing:(__unused UITextView *)textView
148+
{
149+
[_backedTextInput.textInputDelegate textInputDidBeginEditing];
150+
}
151+
152+
- (BOOL)textViewShouldEndEditing:(__unused UITextView *)textView
153+
{
154+
return [_backedTextInput.textInputDelegate textInputShouldEndEditing];
155+
}
156+
157+
- (void)textViewDidEndEditing:(__unused UITextView *)textView
158+
{
159+
[_backedTextInput.textInputDelegate textInputDidEndEditing];
160+
}
161+
162+
- (BOOL)textView:(__unused UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
163+
{
164+
return [_backedTextInput.textInputDelegate textInputShouldChangeTextInRange:range replacementText:text];
165+
}
166+
167+
- (void)textViewDidChange:(__unused UITextView *)textView
168+
{
169+
[_backedTextInput.textInputDelegate textInputDidChange];
170+
}
171+
172+
- (void)textViewDidChangeSelection:(__unused UITextView *)textView
173+
{
174+
[_backedTextInput.textInputDelegate textInputDidChangeSelection];
175+
}
176+
177+
@end

Libraries/Text/RCTBackedTextInputViewProtocol.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
#import <UIKit/UIKit.h>
1111

12+
@protocol RCTBackedTextInputDelegate;
13+
1214
@protocol RCTBackedTextInputViewProtocol <UITextInput>
1315

1416
@property (nonatomic, copy, nullable) NSString *text;
@@ -19,5 +21,6 @@
1921
@property (nonatomic, strong, nullable) UIFont *font;
2022
@property (nonatomic, assign) UIEdgeInsets textContainerInset;
2123
@property (nonatomic, strong, nullable) UIView *inputAccessoryView;
24+
@property (nonatomic, weak, nullable) id<RCTBackedTextInputDelegate> textInputDelegate;
2225

2326
@end

Libraries/Text/RCTText.xcodeproj/project.pbxproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
58B511D01A9E6C5C00147676 /* RCTShadowText.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B511CB1A9E6C5C00147676 /* RCTShadowText.m */; };
2828
58B511D11A9E6C5C00147676 /* RCTTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B511CD1A9E6C5C00147676 /* RCTTextManager.m */; };
2929
58B512161A9E6EFF00147676 /* RCTText.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B512141A9E6EFF00147676 /* RCTText.m */; };
30+
598F41261F145D4900B8495B /* RCTBackedTextInputDelegateAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 598F41251F145D4900B8495B /* RCTBackedTextInputDelegateAdapter.m */; };
31+
598F41271F145D4900B8495B /* RCTBackedTextInputDelegateAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 598F41251F145D4900B8495B /* RCTBackedTextInputDelegateAdapter.m */; };
3032
599DF25F1F0306660079B53E /* RCTBackedTextInputViewProtocol.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 599DF25D1F0304B30079B53E /* RCTBackedTextInputViewProtocol.h */; };
3133
599DF2611F0306C30079B53E /* RCTBackedTextInputViewProtocol.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 599DF25D1F0304B30079B53E /* RCTBackedTextInputViewProtocol.h */; };
3234
599DF2641F03076D0079B53E /* RCTTextInput.m in Sources */ = {isa = PBXBuildFile; fileRef = 599DF2631F03076D0079B53E /* RCTTextInput.m */; };
@@ -95,6 +97,9 @@
9597
58B511CD1A9E6C5C00147676 /* RCTTextManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextManager.m; sourceTree = "<group>"; };
9698
58B512141A9E6EFF00147676 /* RCTText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTText.m; sourceTree = "<group>"; };
9799
58B512151A9E6EFF00147676 /* RCTText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTText.h; sourceTree = "<group>"; };
100+
598F41231F145D4900B8495B /* RCTBackedTextInputDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBackedTextInputDelegate.h; sourceTree = "<group>"; };
101+
598F41241F145D4900B8495B /* RCTBackedTextInputDelegateAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBackedTextInputDelegateAdapter.h; sourceTree = "<group>"; };
102+
598F41251F145D4900B8495B /* RCTBackedTextInputDelegateAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBackedTextInputDelegateAdapter.m; sourceTree = "<group>"; };
98103
599DF25D1F0304B30079B53E /* RCTBackedTextInputViewProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBackedTextInputViewProtocol.h; sourceTree = "<group>"; };
99104
599DF2621F03076D0079B53E /* RCTTextInput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTextInput.h; sourceTree = "<group>"; };
100105
599DF2631F03076D0079B53E /* RCTTextInput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextInput.m; sourceTree = "<group>"; };
@@ -115,6 +120,9 @@
115120
isa = PBXGroup;
116121
children = (
117122
58B5119C1A9E6C1200147676 /* Products */,
123+
598F41231F145D4900B8495B /* RCTBackedTextInputDelegate.h */,
124+
598F41241F145D4900B8495B /* RCTBackedTextInputDelegateAdapter.h */,
125+
598F41251F145D4900B8495B /* RCTBackedTextInputDelegateAdapter.m */,
118126
599DF25D1F0304B30079B53E /* RCTBackedTextInputViewProtocol.h */,
119127
AF3225F71DE5574F00D3E7E7 /* RCTConvert+Text.h */,
120128
AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */,
@@ -244,6 +252,7 @@
244252
2D3B5F361D9B106F00451313 /* RCTShadowText.m in Sources */,
245253
2D3B5F3B1D9B106F00451313 /* RCTTextView.m in Sources */,
246254
59AF89AB1EDCBCC700F004B1 /* RCTUITextField.m in Sources */,
255+
598F41271F145D4900B8495B /* RCTBackedTextInputDelegateAdapter.m in Sources */,
247256
2D3B5F3A1D9B106F00451313 /* RCTTextFieldManager.m in Sources */,
248257
599DF2651F03076D0079B53E /* RCTTextInput.m in Sources */,
249258
2D3B5F341D9B103100451313 /* RCTRawTextManager.m in Sources */,
@@ -267,6 +276,7 @@
267276
19FC5C851D41A4120090108F /* RCTTextSelection.m in Sources */,
268277
1362F1001B4D51F400E06D8C /* RCTTextField.m in Sources */,
269278
59AF89AA1EDCBCC700F004B1 /* RCTUITextField.m in Sources */,
279+
598F41261F145D4900B8495B /* RCTBackedTextInputDelegateAdapter.m in Sources */,
270280
58B512161A9E6EFF00147676 /* RCTText.m in Sources */,
271281
599DF2641F03076D0079B53E /* RCTTextInput.m in Sources */,
272282
1362F1011B4D51F400E06D8C /* RCTTextFieldManager.m in Sources */,

Libraries/Text/RCTTextField.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,4 @@
2626

2727
@property (nonatomic, copy) RCTDirectEventBlock onSelectionChange;
2828

29-
@property (nonatomic, strong) RCTUITextField *textField;
30-
3129
@end

0 commit comments

Comments
 (0)