|
8 | 8 | namespace facebook::react { |
9 | 9 |
|
10 | 10 | extern const char EnrichedTextInputViewComponentName[] = "EnrichedTextInputView"; |
| 11 | +id EnrichedTextInputViewShadowNode::mockTextInputView_ = nullptr; |
11 | 12 |
|
12 | 13 | EnrichedTextInputViewShadowNode::EnrichedTextInputViewShadowNode( |
13 | 14 | const ShadowNodeFragment& fragment, |
14 | 15 | const ShadowNodeFamily::Shared& family, |
15 | 16 | ShadowNodeTraits traits |
16 | 17 | ): ConcreteViewShadowNode(fragment, family, traits) { |
17 | 18 | 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]; |
18 | 38 | } |
19 | 39 |
|
20 | 40 | EnrichedTextInputViewShadowNode::EnrichedTextInputViewShadowNode( |
|
44 | 64 | EnrichedTextInputView *typedComponentObject = (EnrichedTextInputView *) componentObject; |
45 | 65 |
|
46 | 66 | 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 | + |
47 | 72 | __block CGSize estimatedSize; |
48 | 73 |
|
49 | 74 | // synchronously dispatch to main thread if needed |
|
61 | 86 | }; |
62 | 87 | } |
63 | 88 | } 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 | + }; |
81 | 108 | } |
82 | 109 |
|
83 | 110 | return Size(); |
|
0 commit comments