Skip to content

Commit cb3691e

Browse files
committed
feat: mock the input for proper first renders measurements
1 parent 0db5cfe commit cb3691e

2 files changed

Lines changed: 46 additions & 17 deletions

File tree

ios/internals/EnrichedTextInputViewShadowNode.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class EnrichedTextInputViewShadowNode : public ConcreteViewShadowNode<
3535

3636
private:
3737
int localForceHeightRecalculationCounter_;
38+
static id mockTextInputView_;
39+
void setupMockTextInputView_();
3840
};
3941

4042
} // namespace facebook::react

ios/internals/EnrichedTextInputViewShadowNode.mm

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,33 @@
88
namespace facebook::react {
99

1010
extern const char EnrichedTextInputViewComponentName[] = "EnrichedTextInputView";
11+
id EnrichedTextInputViewShadowNode::mockTextInputView_ = nullptr;
1112

1213
EnrichedTextInputViewShadowNode::EnrichedTextInputViewShadowNode(
1314
const ShadowNodeFragment& fragment,
1415
const ShadowNodeFamily::Shared& family,
1516
ShadowNodeTraits traits
1617
): ConcreteViewShadowNode(fragment, family, traits) {
1718
localForceHeightRecalculationCounter_ = 0;
19+
20+
// mock text input needs to be initialized on the main thread
21+
if([NSThread isMainThread]) {
22+
setupMockTextInputView_();
23+
} else {
24+
dispatch_sync(dispatch_get_main_queue(), ^{
25+
setupMockTextInputView_();
26+
});
27+
}
28+
}
29+
30+
// mock input is used for the first measure calls that need to be done when the real input isn't defined yet
31+
void EnrichedTextInputViewShadowNode::setupMockTextInputView_() {
32+
// it's rendered far away from the viewport
33+
const int veryFarAway = 20000;
34+
const int mockSize = 1000;
35+
mockTextInputView_ = [[EnrichedTextInputView alloc] initWithFrame:(CGRectMake(veryFarAway, veryFarAway, mockSize, mockSize))];
36+
const auto props = this->getProps();
37+
[mockTextInputView_ updateProps:props oldProps:nullptr];
1838
}
1939

2040
EnrichedTextInputViewShadowNode::EnrichedTextInputViewShadowNode(
@@ -44,6 +64,11 @@
4464
EnrichedTextInputView *typedComponentObject = (EnrichedTextInputView *) componentObject;
4565

4666
if(typedComponentObject != nullptr) {
67+
// remove the mock input on the first render with a defined real input
68+
if(mockTextInputView_ != nullptr) {
69+
mockTextInputView_ = nullptr;
70+
}
71+
4772
__block CGSize estimatedSize;
4873

4974
// synchronously dispatch to main thread if needed
@@ -61,23 +86,25 @@
6186
};
6287
}
6388
} else {
64-
// on the very first call there is no componentView that we can query for the component height
65-
// thus, a little heuristic: just put a height that is exactly height of letter "I" with default apple font and size from props
66-
// in a lot of cases it will be the desired height
67-
// in others, the jump on the second call will at least be smaller
68-
const auto props = this->getProps();
69-
const auto &typedProps = *std::static_pointer_cast<EnrichedTextInputViewProps const>(props);
70-
NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:@"I" attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:typedProps.fontSize]}];
71-
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrStr);
72-
const CGSize &suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(
73-
framesetter,
74-
CFRangeMake(0, 1),
75-
nullptr,
76-
CGSizeMake(layoutConstraints.maximumSize.width, DBL_MAX),
77-
nullptr
78-
);
79-
80-
return {suggestedSize.width, suggestedSize.height};
89+
if(mockTextInputView_ == nullptr) {
90+
return Size();
91+
}
92+
93+
__block CGSize estimatedSize;
94+
95+
// synchronously dispatch to main thread if needed
96+
if([NSThread isMainThread]) {
97+
estimatedSize = [mockTextInputView_ measureSize:layoutConstraints.maximumSize.width];
98+
} else {
99+
dispatch_sync(dispatch_get_main_queue(), ^{
100+
estimatedSize = [mockTextInputView_ measureSize:layoutConstraints.maximumSize.width];
101+
});
102+
}
103+
104+
return {
105+
estimatedSize.width,
106+
MIN(estimatedSize.height, layoutConstraints.maximumSize.height)
107+
};
81108
}
82109

83110
return Size();

0 commit comments

Comments
 (0)