@@ -159,6 +159,11 @@ - (instancetype)initWithFrame:(CGRect)frame
159159{
160160 if ((self = [super initWithFrame: frame])) {
161161 [self .panGestureRecognizer addTarget: self action: @selector (handleCustomPan: )];
162+
163+ // We intentionaly force `UIScrollView`s `semanticContentAttribute` to `LTR` here
164+ // because this attribute affects a position of vertical scrollbar; we don't want this
165+ // scrollbar flip because we also flip it with whole `UIScrollView` flip.
166+ self.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight;
162167 }
163168 return self;
164169}
@@ -191,7 +196,7 @@ - (void)handleCustomPan:(__unused UIPanGestureRecognizer *)sender
191196 self.panGestureRecognizer .enabled = YES ;
192197 // TODO: If mid bounce, animate the scroll view to a non-bounced position
193198 // while disabling (but only if `stopScrollInteractionIfJSHasResponder` was
194- // called *during* a `pan`. Currently, it will just snap into place which
199+ // called *during* a `pan`) . Currently, it will just snap into place which
195200 // is not so bad either.
196201 // Another approach:
197202 // self.scrollEnabled = NO;
@@ -278,9 +283,10 @@ - (void)dockClosestSectionHeader
278283{
279284 UIView *contentView = [self contentView ];
280285 CGFloat scrollTop = self.bounds .origin .y + self.contentInset .top ;
286+
287+ #if !TARGET_OS_TV
281288 // If the RefreshControl is refreshing, remove it's height so sticky headers are
282289 // positioned properly when scrolling down while refreshing.
283- #if !TARGET_OS_TV
284290 if (_rctRefreshControl != nil && _rctRefreshControl.refreshing ) {
285291 scrollTop -= _rctRefreshControl.frame .size .height ;
286292 }
@@ -451,6 +457,21 @@ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
451457RCT_NOT_IMPLEMENTED (- (instancetype )initWithFrame:(CGRect)frame)
452458RCT_NOT_IMPLEMENTED(- (instancetype )initWithCoder:(NSCoder *)aDecoder)
453459
460+ static inline void RCTApplyTranformationAccordingLayoutDirection(UIView *view, UIUserInterfaceLayoutDirection layoutDirection) {
461+ view.transform =
462+ layoutDirection == UIUserInterfaceLayoutDirectionLeftToRight ?
463+ CGAffineTransformIdentity :
464+ CGAffineTransformMakeScale (-1 , 1 );
465+ }
466+
467+ - (void )setReactLayoutDirection : (UIUserInterfaceLayoutDirection)layoutDirection
468+ {
469+ [super setReactLayoutDirection: layoutDirection];
470+
471+ RCTApplyTranformationAccordingLayoutDirection (_scrollView, layoutDirection);
472+ RCTApplyTranformationAccordingLayoutDirection (_contentView, layoutDirection);
473+ }
474+
454475- (void )setRemoveClippedSubviews : (__unused BOOL )removeClippedSubviews
455476{
456477 // Does nothing
@@ -467,6 +488,7 @@ - (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex
467488 {
468489 RCTAssert (_contentView == nil , @" RCTScrollView may only contain a single subview" );
469490 _contentView = view;
491+ RCTApplyTranformationAccordingLayoutDirection (_contentView, self.reactLayoutDirection );
470492 [_scrollView addSubview: view];
471493 }
472494}
@@ -921,16 +943,9 @@ - (CGSize)contentSize
921943{
922944 if (!CGSizeEqualToSize (_contentSize, CGSizeZero)) {
923945 return _contentSize;
924- } else if (!_contentView) {
925- return CGSizeZero;
926- } else {
927- CGSize singleSubviewSize = _contentView.frame .size ;
928- CGPoint singleSubviewPosition = _contentView.frame .origin ;
929- return (CGSize){
930- singleSubviewSize.width + singleSubviewPosition.x ,
931- singleSubviewSize.height + singleSubviewPosition.y
932- };
933946 }
947+
948+ return _contentView.frame .size ;
934949}
935950
936951- (void )reactBridgeDidFinishTransaction
0 commit comments