From 9c804fc9e9cbf307904533e698783703b42f69f5 Mon Sep 17 00:00:00 2001 From: Kacper Rozniata <56474758+krozniata@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:23:41 +0100 Subject: [PATCH] fix(paper): prevent freeze on layout update during transition --- ios/RNCPagerView.h | 8 ++++---- ios/RNCPagerView.m | 24 +++++++++++++++--------- ios/RNCPagerViewManager.m | 10 ++++++++-- ios/RNCPagerViewShadowView.h | 5 +++++ ios/RNCPagerViewShadowView.m | 22 ++++++++++++++++++++++ 5 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 ios/RNCPagerViewShadowView.h create mode 100644 ios/RNCPagerViewShadowView.m diff --git a/ios/RNCPagerView.h b/ios/RNCPagerView.h index e523d230..18b0149a 100644 --- a/ios/RNCPagerView.h +++ b/ios/RNCPagerView.h @@ -1,14 +1,14 @@ +#import #import #import -#import #import #import "UIView+isHorizontalRtlLayout.h" NS_ASSUME_NONNULL_BEGIN -@interface RNCPagerView: UIView +@interface RNCPagerView: RCTView -- (instancetype)initWithEventDispatcher:(id )eventDispatcher; +- (instancetype)initWithBridge:(RCTBridge *)bridge; @property(nonatomic) NSInteger initialPage; @property(nonatomic) NSInteger lastReportedIndex; @@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, copy) RCTDirectEventBlock onPageScrollStateChanged; @property(nonatomic) BOOL overdrag; @property(nonatomic) NSString* layoutDirection; -@property(nonatomic, assign) BOOL animating; +@property(nonatomic, assign) BOOL transitioning; - (void)goTo:(NSInteger)index animated:(BOOL)animated; - (void)shouldScroll:(BOOL)scrollEnabled; diff --git a/ios/RNCPagerView.m b/ios/RNCPagerView.m index 7d99becc..ee646d7f 100644 --- a/ios/RNCPagerView.m +++ b/ios/RNCPagerView.m @@ -1,6 +1,7 @@ #import "RNCPagerView.h" #import "React/RCTLog.h" #import +#import #import "UIViewController+CreateExtension.h" #import "RCTOnPageScrollEvent.h" @@ -13,7 +14,7 @@ @interface RNCPagerView () eventDispatcher; @property(nonatomic, weak) UIScrollView *scrollView; @property(nonatomic, weak) UIView *currentView; @@ -30,10 +31,12 @@ - (void)shouldDismissKeyboard:(NSString *)dismissKeyboard; @implementation RNCPagerView { uint16_t _coalescingKey; + __weak RCTBridge * _bridge; } -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher { +- (instancetype)initWithBridge:(RCTBridge *)bridge { if (self = [super init]) { + _bridge = bridge; _scrollEnabled = YES; _pageMargin = 0; _lastReportedIndex = -1; @@ -44,7 +47,7 @@ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher { _dismissKeyboard = UIScrollViewKeyboardDismissModeNone; #endif _coalescingKey = 0; - _eventDispatcher = eventDispatcher; + _eventDispatcher = bridge.eventDispatcher; _cachedControllers = [NSHashTable hashTableWithOptions:NSHashTableStrongMemory]; _overdrag = NO; _layoutDirection = @"ltr"; @@ -178,7 +181,7 @@ - (void)setReactViewControllers:(NSInteger)index uint16_t coalescingKey = _coalescingKey++; if (animated == YES) { - self.animating = YES; + [self setTransitioning:YES]; } [self.reactPageViewController setViewControllers:@[controller] @@ -190,10 +193,8 @@ - (void)setReactViewControllers:(NSInteger)index strongSelf.currentView = controller.view; [strongSelf enableSwipe]; - - if (finished) { - strongSelf.animating = NO; - } + + [strongSelf setTransitioning:NO]; if (strongSelf.eventDispatcher) { if (strongSelf.lastReportedIndex != strongSelf.currentIndex) { @@ -245,6 +246,11 @@ - (void)enableSwipe { self.reactPageViewController.view.userInteractionEnabled = YES; } +- (void)setTransitioning:(BOOL)transitioning { + _transitioning = transitioning; + [_bridge.uiManager setLocalData:@{@"transitioning": @(transitioning)} forView:self]; +} + - (void)goTo:(NSInteger)index animated:(BOOL)animated { NSInteger numberOfPages = self.reactSubviews.count; @@ -267,7 +273,7 @@ - (void)goTo:(NSInteger)index animated:(BOOL)animated { long diff = labs(index - _currentIndex); - [self goToViewController:index direction:direction animated:(!self.animating && animated) shouldCallOnPageSelected: YES]; + [self goToViewController:index direction:direction animated:(!_transitioning && animated) shouldCallOnPageSelected: YES]; if (diff == 0) { [self goToViewController:index direction:direction animated:NO shouldCallOnPageSelected:YES]; diff --git a/ios/RNCPagerViewManager.m b/ios/RNCPagerViewManager.m index 90404371..131db168 100644 --- a/ios/RNCPagerViewManager.m +++ b/ios/RNCPagerViewManager.m @@ -1,5 +1,6 @@ #import "RNCPagerViewManager.h" +#import "RNCPagerViewShadowView.h" @implementation RNCPagerViewManager @@ -30,7 +31,7 @@ - (void) goToPage RCTLogError(@"Cannot find RNCPagerView with tag #%@", reactTag); return; } - if (!animated || !view.animating) { + if (!animated || !view.transitioning) { [view goTo:index.integerValue animated:animated]; } }]; @@ -80,7 +81,12 @@ - (void) changeScrollEnabled - (UIView *)view { - return [[RNCPagerView alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; + return [[RNCPagerView alloc] initWithBridge: self.bridge]; +} + + +- (RCTShadowView *)shadowView { + return [RNCPagerViewShadowView new]; } @end diff --git a/ios/RNCPagerViewShadowView.h b/ios/RNCPagerViewShadowView.h new file mode 100644 index 00000000..74ab3bcf --- /dev/null +++ b/ios/RNCPagerViewShadowView.h @@ -0,0 +1,5 @@ +#import + +@interface RNCPagerViewShadowView : RCTShadowView + +@end diff --git a/ios/RNCPagerViewShadowView.m b/ios/RNCPagerViewShadowView.m new file mode 100644 index 00000000..0c8396cf --- /dev/null +++ b/ios/RNCPagerViewShadowView.m @@ -0,0 +1,22 @@ +#import "RNCPagerViewShadowView.h" + +@implementation RNCPagerViewShadowView { + BOOL _transitioning; +} + +- (void)layoutWithMetrics:(RCTLayoutMetrics)layoutMetrics + layoutContext:(RCTLayoutContext)layoutContext { + if (_transitioning) { + return; + } + + [super layoutWithMetrics:layoutMetrics layoutContext:layoutContext]; +} + +- (void)setLocalData:(NSDictionary *)localData { + [super setLocalData:localData]; + + _transitioning = [localData[@"transitioning"] boolValue]; +} + +@end