Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ios/internals/EnrichedTextInputViewShadowNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class EnrichedTextInputViewShadowNode : public ConcreteViewShadowNode<

private:
int localForceHeightRecalculationCounter_;
static id mockTextInputView_;
void setupMockTextInputView_();
};

} // namespace facebook::react
62 changes: 45 additions & 17 deletions ios/internals/EnrichedTextInputViewShadowNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,34 @@
namespace facebook::react {

extern const char EnrichedTextInputViewComponentName[] = "EnrichedTextInputView";
id EnrichedTextInputViewShadowNode::mockTextInputView_ = nullptr;

EnrichedTextInputViewShadowNode::EnrichedTextInputViewShadowNode(
const ShadowNodeFragment& fragment,
const ShadowNodeFamily::Shared& family,
ShadowNodeTraits traits
): ConcreteViewShadowNode(fragment, family, traits) {
localForceHeightRecalculationCounter_ = 0;

// mock text input needs to be initialized on the main thread
if([NSThread isMainThread]) {
setupMockTextInputView_();
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
setupMockTextInputView_();
});
}
}

// mock input is used for the first measure calls that need to be done when the real input isn't defined yet
void EnrichedTextInputViewShadowNode::setupMockTextInputView_() {
// it's rendered far away from the viewport
const int veryFarAway = 20000;
const int mockSize = 1000;
mockTextInputView_ = [[EnrichedTextInputView alloc] initWithFrame:(CGRectMake(veryFarAway, veryFarAway, mockSize, mockSize))];
const auto props = this->getProps();
((EnrichedTextInputView *)mockTextInputView_)->blockEmitting = YES;
[mockTextInputView_ updateProps:props oldProps:nullptr];
}

EnrichedTextInputViewShadowNode::EnrichedTextInputViewShadowNode(
Expand Down Expand Up @@ -44,6 +65,11 @@
EnrichedTextInputView *typedComponentObject = (EnrichedTextInputView *) componentObject;

if(typedComponentObject != nullptr) {
// remove the mock input on the first render with a defined real input
if(mockTextInputView_ != nullptr) {
mockTextInputView_ = nullptr;
}

__block CGSize estimatedSize;

// synchronously dispatch to main thread if needed
Expand All @@ -61,23 +87,25 @@
};
}
} else {
// on the very first call there is no componentView that we can query for the component height
// thus, a little heuristic: just put a height that is exactly height of letter "I" with default apple font and size from props
// in a lot of cases it will be the desired height
// in others, the jump on the second call will at least be smaller
const auto props = this->getProps();
const auto &typedProps = *std::static_pointer_cast<EnrichedTextInputViewProps const>(props);
NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:@"I" attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:typedProps.fontSize]}];
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrStr);
const CGSize &suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(
framesetter,
CFRangeMake(0, 1),
nullptr,
CGSizeMake(layoutConstraints.maximumSize.width, DBL_MAX),
nullptr
);

return {suggestedSize.width, suggestedSize.height};
if(mockTextInputView_ == nullptr) {
return Size();
}

__block CGSize estimatedSize;

// synchronously dispatch to main thread if needed
if([NSThread isMainThread]) {
estimatedSize = [mockTextInputView_ measureSize:layoutConstraints.maximumSize.width];
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
estimatedSize = [mockTextInputView_ measureSize:layoutConstraints.maximumSize.width];
});
}

return {
estimatedSize.width,
MIN(estimatedSize.height, layoutConstraints.maximumSize.height)
};
}

return Size();
Expand Down
Loading