From 8911fa3acca280358255518f504ee6696bf0260e Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sat, 4 Nov 2023 10:01:40 +0000 Subject: [PATCH 01/63] Added bottom sheet view and manager ios files --- NavigationReactNative/src/BottomSheet.tsx | 1 - .../src/ios/NVBottomSheetManager.m | 18 ++++++++++++++++++ .../src/ios/NVBottomSheetView.h | 5 +++++ .../src/ios/NVBottomSheetView.m | 12 ++++++++++++ .../project.pbxproj | 6 ++++++ 5 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 NavigationReactNative/src/ios/NVBottomSheetManager.m create mode 100644 NavigationReactNative/src/ios/NVBottomSheetView.h create mode 100644 NavigationReactNative/src/ios/NVBottomSheetView.m diff --git a/NavigationReactNative/src/BottomSheet.tsx b/NavigationReactNative/src/BottomSheet.tsx index ac5ecaeed1..3c98d35a5c 100644 --- a/NavigationReactNative/src/BottomSheet.tsx +++ b/NavigationReactNative/src/BottomSheet.tsx @@ -39,7 +39,6 @@ class BottomSheet extends React.Component { } } render() { - if (Platform.OS === 'ios') return null; var { expandedHeight, expandedOffset, peekHeight, halfExpandedRatio, hideable, skipCollapsed, draggable, children } = this.props const detents = (UIManager as any).getViewManagerConfig('NVBottomSheet').Constants.Detent return ( diff --git a/NavigationReactNative/src/ios/NVBottomSheetManager.m b/NavigationReactNative/src/ios/NVBottomSheetManager.m new file mode 100644 index 0000000000..e9da6f1c34 --- /dev/null +++ b/NavigationReactNative/src/ios/NVBottomSheetManager.m @@ -0,0 +1,18 @@ +#import "NVBottomSheetView.h" + +#import +#import + +@interface NVBottomSheetManager : RCTViewManager + +@end + +@implementation NVBottomSheetManager + +RCT_EXPORT_MODULE() + +- (UIView *)view { + return [[NVBottomSheetView alloc] init]; +} + +@end diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.h b/NavigationReactNative/src/ios/NVBottomSheetView.h new file mode 100644 index 0000000000..8fb54e3d5e --- /dev/null +++ b/NavigationReactNative/src/ios/NVBottomSheetView.h @@ -0,0 +1,5 @@ +#import + +@interface NVBottomSheetView : UIView + +@end diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m new file mode 100644 index 0000000000..8ce67f3bcb --- /dev/null +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -0,0 +1,12 @@ +#import "NVBottomSheetView.h" + +@implementation NVBottomSheetView + +- (id)init +{ + if (self = [super init]) { + } + return self; +} + +@end diff --git a/NavigationReactNative/src/ios/NavigationReactNative.xcodeproj/project.pbxproj b/NavigationReactNative/src/ios/NavigationReactNative.xcodeproj/project.pbxproj index a585db4455..cc607e1ed0 100644 --- a/NavigationReactNative/src/ios/NavigationReactNative.xcodeproj/project.pbxproj +++ b/NavigationReactNative/src/ios/NavigationReactNative.xcodeproj/project.pbxproj @@ -58,6 +58,9 @@ 706F41012562BF7D000E213E /* NVStatusBarView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NVStatusBarView.m; sourceTree = ""; }; 706F41042562BF95000E213E /* NVStatusBarView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NVStatusBarView.h; sourceTree = ""; }; 706F41052562BFAC000E213E /* NVStatusBarManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NVStatusBarManager.m; sourceTree = ""; }; + 707C4BF62AF64BCD008FC72E /* NVBottomSheetView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NVBottomSheetView.h; sourceTree = ""; }; + 707C4BF72AF64BE6008FC72E /* NVBottomSheetView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NVBottomSheetView.m; sourceTree = ""; }; + 707C4BF82AF64BF6008FC72E /* NVBottomSheetManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NVBottomSheetManager.m; sourceTree = ""; }; 707E82C828B00EA4006D9ED0 /* NVMaterial3Module.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NVMaterial3Module.mm; sourceTree = ""; }; 707E82C928B00EA4006D9ED0 /* NVMaterial3Module.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NVMaterial3Module.h; sourceTree = ""; }; 707F41AF24A74F4600A06BF2 /* NVTabBarPagerView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NVTabBarPagerView.m; sourceTree = ""; }; @@ -139,6 +142,9 @@ 7020F6B520ECD07A00E7A74E = { isa = PBXGroup; children = ( + 707C4BF82AF64BF6008FC72E /* NVBottomSheetManager.m */, + 707C4BF72AF64BE6008FC72E /* NVBottomSheetView.m */, + 707C4BF62AF64BCD008FC72E /* NVBottomSheetView.h */, 709C5C22298EAD4900DF5B14 /* NVSearchResultsComponentView.h */, 709C5C23298EAE0A00DF5B14 /* NVSearchToolbarComponentView.mm */, 709C5C24298EAE1700DF5B14 /* NVSearchToolbarComponentView.h */, From de06b63ca4dcb525432ca8e083ee01b4ce5d1f66 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sat, 4 Nov 2023 10:33:06 +0000 Subject: [PATCH 02/63] Removed unsupported view manager config on ios --- NavigationReactNative/src/BottomSheet.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/NavigationReactNative/src/BottomSheet.tsx b/NavigationReactNative/src/BottomSheet.tsx index 3c98d35a5c..ed05073cf7 100644 --- a/NavigationReactNative/src/BottomSheet.tsx +++ b/NavigationReactNative/src/BottomSheet.tsx @@ -40,11 +40,12 @@ class BottomSheet extends React.Component { } render() { var { expandedHeight, expandedOffset, peekHeight, halfExpandedRatio, hideable, skipCollapsed, draggable, children } = this.props - const detents = (UIManager as any).getViewManagerConfig('NVBottomSheet').Constants.Detent + var { selectedDetent } = this.state; + const constants = (UIManager as any).getViewManagerConfig('NVBottomSheet').Constants; return ( Date: Sat, 4 Nov 2023 12:11:29 +0000 Subject: [PATCH 03/63] Logged out the height of the sheet as it animates Nothing was called when the sheet was animated - not viewDidLayoutSubviews. The frame size didn't even change - tried logging it out. But managed to get it on the presentationLayer! Got the [clue from react native screens ](https://github.com/software-mansion/react-native-screens/pull/1870/files#diff-ad35cf2dc9a86855afc1d2443a42a133ccf0dd8b17589719cb1aab4a81d20604R127) --- .../src/ios/NVBottomSheetView.m | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 8ce67f3bcb..6afeb51129 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -1,12 +1,60 @@ #import "NVBottomSheetView.h" +#import +#import "UIView+React.h" + +@interface NVBottomSheetController : UIViewController + +@end + +@implementation NVBottomSheetController + +@end + @implementation NVBottomSheetView +{ + NVBottomSheetController *_bottomSheetController; + int _oldHeight; +} - (id)init { if (self = [super init]) { + _bottomSheetController = [[NVBottomSheetController alloc] init]; + CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateView)]; + [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; } return self; } +- (void)updateView { + int newHeight = [[_bottomSheetController.view.layer.presentationLayer valueForKeyPath:@"frame.size.height"] intValue]; + if (_oldHeight != newHeight) { + NSLog(@"animated value: %d\n", newHeight); + _oldHeight = newHeight; + } +} + +- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex +{ + [super insertReactSubview:subview atIndex:atIndex]; + _bottomSheetController.view = subview; +} + +- (void)removeReactSubview:(UIView *)subview +{ + [super removeReactSubview:subview]; +} + +- (void)didMoveToWindow +{ + [super didMoveToWindow]; + if (@available(iOS 15.0, *)) { + UISheetPresentationController *sheet = _bottomSheetController.sheetPresentationController; + [sheet setDetents:@[UISheetPresentationControllerDetent.mediumDetent, UISheetPresentationControllerDetent.largeDetent]]; + [[self reactViewController] presentViewController:_bottomSheetController animated:true completion:nil]; + } +} + @end + From dad5e0ee05ab43a3f43f51d0531dbdbf0ad2db47 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sat, 4 Nov 2023 21:26:25 +0000 Subject: [PATCH 04/63] Used UIViewController directly for simplicity --- NavigationReactNative/src/ios/NVBottomSheetView.m | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 6afeb51129..a8a6eb3384 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -3,24 +3,16 @@ #import #import "UIView+React.h" -@interface NVBottomSheetController : UIViewController - -@end - -@implementation NVBottomSheetController - -@end - @implementation NVBottomSheetView { - NVBottomSheetController *_bottomSheetController; + UIViewController *_bottomSheetController; int _oldHeight; } - (id)init { if (self = [super init]) { - _bottomSheetController = [[NVBottomSheetController alloc] init]; + _bottomSheetController = [[UIViewController alloc] init]; CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateView)]; [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; } From 25ba84e0695bc5ba5ad308cdc467a73ecf5ef82e Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 5 Nov 2023 09:45:09 +0000 Subject: [PATCH 05/63] Resized content based on sheet position Had to use a container view (got [the idea from React Native's Modal](https://github.com/facebook/react-native/blob/d077239fff600d41d093dfeca87f6744bd7f1cd3/packages/react-native/React/Views/RCTModalHostView.m#L38)). Without the container couldn't resize it - it worked when dragging but not when dropping. All the measurements went out. Guessing that resizing while it's being animated is bad. So used the container view as the animated view and resized the content. It works! but it's jumpy when dragging. --- NavigationReactNative/src/BottomSheet.tsx | 2 +- .../src/ios/NVBottomSheetManager.m | 2 +- .../src/ios/NVBottomSheetView.h | 3 ++ .../src/ios/NVBottomSheetView.m | 31 ++++++++++++++----- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/NavigationReactNative/src/BottomSheet.tsx b/NavigationReactNative/src/BottomSheet.tsx index ed05073cf7..b38fcf0e2a 100644 --- a/NavigationReactNative/src/BottomSheet.tsx +++ b/NavigationReactNative/src/BottomSheet.tsx @@ -61,7 +61,7 @@ class BottomSheet extends React.Component { styles.bottomSheet, expandedHeight != null ? { height: expandedHeight } : null, expandedOffset != null ? { top: expandedOffset } : null, - expandedHeight == null && expandedOffset == null ? { top: 0 } : null + expandedHeight == null && expandedOffset == null ? { top: undefined } : null ]} > {children} diff --git a/NavigationReactNative/src/ios/NVBottomSheetManager.m b/NavigationReactNative/src/ios/NVBottomSheetManager.m index e9da6f1c34..fe4b2485b3 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetManager.m +++ b/NavigationReactNative/src/ios/NVBottomSheetManager.m @@ -12,7 +12,7 @@ @implementation NVBottomSheetManager RCT_EXPORT_MODULE() - (UIView *)view { - return [[NVBottomSheetView alloc] init]; + return [[NVBottomSheetView alloc] initWithBridge:self.bridge]; } @end diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.h b/NavigationReactNative/src/ios/NVBottomSheetView.h index 8fb54e3d5e..13c1145ec9 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetView.h @@ -1,5 +1,8 @@ #import +#import @interface NVBottomSheetView : UIView +-(id)initWithBridge: (RCTBridge *)bridge; + @end diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index a8a6eb3384..37ce7179d4 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -1,18 +1,26 @@ #import "NVBottomSheetView.h" #import -#import "UIView+React.h" +#import +#import +#import @implementation NVBottomSheetView { + __weak RCTBridge *_bridge; UIViewController *_bottomSheetController; - int _oldHeight; + UIView *_reactSubview; + CGSize _oldSize; } -- (id)init +- (id)initWithBridge:(RCTBridge *)bridge { if (self = [super init]) { + _bridge = bridge; _bottomSheetController = [[UIViewController alloc] init]; + UIView *containerView = [UIView new]; + containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + _bottomSheetController.view = containerView; CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateView)]; [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; } @@ -20,22 +28,29 @@ - (id)init } - (void)updateView { - int newHeight = [[_bottomSheetController.view.layer.presentationLayer valueForKeyPath:@"frame.size.height"] intValue]; - if (_oldHeight != newHeight) { - NSLog(@"animated value: %d\n", newHeight); - _oldHeight = newHeight; + CGSize newSize = [[_bottomSheetController.view.layer.presentationLayer valueForKeyPath:@"frame.size"] CGSizeValue]; + if (_oldSize.height != newSize.height) { + NSLog(@"animated value: %f\n", newSize.height); + _oldSize = newSize; + [_bridge.uiManager setSize:newSize forView:_reactSubview]; } } - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex { [super insertReactSubview:subview atIndex:atIndex]; - _bottomSheetController.view = subview; + [_bottomSheetController.view insertSubview:subview atIndex:0]; + _reactSubview = subview; } - (void)removeReactSubview:(UIView *)subview { [super removeReactSubview:subview]; + _reactSubview = nil; +} + +- (void)didUpdateReactSubviews +{ } - (void)didMoveToWindow From 594aad2682d364a46c175fbd5ff254e56da80828 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 5 Nov 2023 10:43:08 +0000 Subject: [PATCH 06/63] Compared old and new size --- NavigationReactNative/src/ios/NVBottomSheetView.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 37ce7179d4..e96fd062d0 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -29,8 +29,7 @@ - (id)initWithBridge:(RCTBridge *)bridge - (void)updateView { CGSize newSize = [[_bottomSheetController.view.layer.presentationLayer valueForKeyPath:@"frame.size"] CGSizeValue]; - if (_oldSize.height != newSize.height) { - NSLog(@"animated value: %f\n", newSize.height); + if (!CGSizeEqualToSize(_oldSize, newSize)) { _oldSize = newSize; [_bridge.uiManager setSize:newSize forView:_reactSubview]; } From 4d14e66d3f8586ca44e47ac9f6c13b56ff63ea92 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 5 Nov 2023 22:52:08 +0000 Subject: [PATCH 07/63] Resized when bounds did change too For smoothness, need the bounds did change resize for when dragging and the animated resize for when dropping. Need to find a way so they only run for their own use-case. Having them both running at the same time causes flickers --- .../src/ios/NVBottomSheetView.m | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index e96fd062d0..b209cf6c4b 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -5,10 +5,33 @@ #import #import + +@interface NVBottomSheetController : UIViewController + +@property (nonatomic, copy) void (^boundsDidChangeBlock)(CGRect newBounds); + +@end + +@implementation NVBottomSheetController +{ + CGRect _lastViewFrame; +} + +- (void)viewDidLayoutSubviews +{ + [super viewDidLayoutSubviews]; + if (self.boundsDidChangeBlock && !CGRectEqualToRect(_lastViewFrame, self.view.frame)) { + self.boundsDidChangeBlock(self.view.bounds); + _lastViewFrame = self.view.frame; + } +} + +@end + @implementation NVBottomSheetView { __weak RCTBridge *_bridge; - UIViewController *_bottomSheetController; + NVBottomSheetController *_bottomSheetController; UIView *_reactSubview; CGSize _oldSize; } @@ -17,16 +40,27 @@ - (id)initWithBridge:(RCTBridge *)bridge { if (self = [super init]) { _bridge = bridge; - _bottomSheetController = [[UIViewController alloc] init]; + _bottomSheetController = [[NVBottomSheetController alloc] init]; UIView *containerView = [UIView new]; containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; _bottomSheetController.view = containerView; CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateView)]; [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + __weak typeof(self) weakSelf = self; + _bottomSheetController.boundsDidChangeBlock = ^(CGRect newBounds) { + [weakSelf notifyForBoundsChange:newBounds]; + }; } return self; } +- (void)notifyForBoundsChange:(CGRect)newBounds +{ + if (_reactSubview) { + [_bridge.uiManager setSize:newBounds.size forView:_reactSubview]; + } +} + - (void)updateView { CGSize newSize = [[_bottomSheetController.view.layer.presentationLayer valueForKeyPath:@"frame.size"] CGSizeValue]; if (!CGSizeEqualToSize(_oldSize, newSize)) { From b537edb69e7b4885d909413efc34426b88ae6927 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 5 Nov 2023 23:50:27 +0000 Subject: [PATCH 08/63] Ensured only one resize runs Don't want both the dragging and the dropping resize to run at the same time. Seems to cause extra flicker. Got the [detection code from react native screens](https://github.com/software-mansion/react-native-screens/blob/2de99533cd716f3757927e0b5a08a4f647a5d1e0/ios/RNSScreen.mm#L112). If there's an animation when frames layout called then it's dropping, otherwise it's dragging --- .../src/ios/NVBottomSheetView.h | 2 +- .../src/ios/NVBottomSheetView.m | 23 +++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.h b/NavigationReactNative/src/ios/NVBottomSheetView.h index 13c1145ec9..9fa8cb8f3b 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetView.h @@ -1,7 +1,7 @@ #import #import -@interface NVBottomSheetView : UIView +@interface NVBottomSheetView : UIView -(id)initWithBridge: (RCTBridge *)bridge; diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index b209cf6c4b..f486575004 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -34,6 +34,7 @@ @implementation NVBottomSheetView NVBottomSheetController *_bottomSheetController; UIView *_reactSubview; CGSize _oldSize; + bool _dragging; } - (id)initWithBridge:(RCTBridge *)bridge @@ -50,6 +51,7 @@ - (id)initWithBridge:(RCTBridge *)bridge _bottomSheetController.boundsDidChangeBlock = ^(CGRect newBounds) { [weakSelf notifyForBoundsChange:newBounds]; }; + _dragging = YES; } return self; } @@ -57,18 +59,35 @@ - (id)initWithBridge:(RCTBridge *)bridge - (void)notifyForBoundsChange:(CGRect)newBounds { if (_reactSubview) { - [_bridge.uiManager setSize:newBounds.size forView:_reactSubview]; + CAAnimation *sizeAnimation = [_bottomSheetController.view.layer animationForKey:@"bounds.size"]; + if (sizeAnimation != nil) { + CABasicAnimation *alongsideAnimation = [CABasicAnimation new]; + alongsideAnimation.duration = sizeAnimation.duration; + alongsideAnimation.beginTime = sizeAnimation.beginTime; + alongsideAnimation.delegate = self; + [_bottomSheetController.view.layer addAnimation:alongsideAnimation forKey:@"alongside"]; + _dragging = NO; + } + if (_dragging) { + [_bridge.uiManager setSize:newBounds.size forView:_reactSubview]; + } } } - (void)updateView { CGSize newSize = [[_bottomSheetController.view.layer.presentationLayer valueForKeyPath:@"frame.size"] CGSizeValue]; - if (!CGSizeEqualToSize(_oldSize, newSize)) { + if (!_dragging && !CGSizeEqualToSize(_oldSize, newSize)) { _oldSize = newSize; [_bridge.uiManager setSize:newSize forView:_reactSubview]; } } + +- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished +{ + _dragging = YES; +} + - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex { [super insertReactSubview:subview atIndex:atIndex]; From 398b05976801bdb9793ad67644d569fd5e0217b4 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 6 Nov 2023 09:15:30 +0000 Subject: [PATCH 09/63] Prevented bounds change resize if dropping Don't think it's any different if the resizes run together. But don't want the bounds resize when the sheet is dropped because that resizes to the destination detent (at the start of the drop). This causes the big glitch - it's hidden by the dropping resize so hard to see - but don't want it to be there --- .../src/ios/NVBottomSheetView.h | 2 +- .../src/ios/NVBottomSheetView.m | 21 +++---------------- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.h b/NavigationReactNative/src/ios/NVBottomSheetView.h index 9fa8cb8f3b..13c1145ec9 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetView.h @@ -1,7 +1,7 @@ #import #import -@interface NVBottomSheetView : UIView +@interface NVBottomSheetView : UIView -(id)initWithBridge: (RCTBridge *)bridge; diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index f486575004..19d0669e97 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -34,7 +34,6 @@ @implementation NVBottomSheetView NVBottomSheetController *_bottomSheetController; UIView *_reactSubview; CGSize _oldSize; - bool _dragging; } - (id)initWithBridge:(RCTBridge *)bridge @@ -51,7 +50,6 @@ - (id)initWithBridge:(RCTBridge *)bridge _bottomSheetController.boundsDidChangeBlock = ^(CGRect newBounds) { [weakSelf notifyForBoundsChange:newBounds]; }; - _dragging = YES; } return self; } @@ -59,16 +57,8 @@ - (id)initWithBridge:(RCTBridge *)bridge - (void)notifyForBoundsChange:(CGRect)newBounds { if (_reactSubview) { - CAAnimation *sizeAnimation = [_bottomSheetController.view.layer animationForKey:@"bounds.size"]; - if (sizeAnimation != nil) { - CABasicAnimation *alongsideAnimation = [CABasicAnimation new]; - alongsideAnimation.duration = sizeAnimation.duration; - alongsideAnimation.beginTime = sizeAnimation.beginTime; - alongsideAnimation.delegate = self; - [_bottomSheetController.view.layer addAnimation:alongsideAnimation forKey:@"alongside"]; - _dragging = NO; - } - if (_dragging) { + CAAnimation *dropping = [_bottomSheetController.view.layer animationForKey:@"bounds.size"]; + if (!dropping) { [_bridge.uiManager setSize:newBounds.size forView:_reactSubview]; } } @@ -76,18 +66,13 @@ - (void)notifyForBoundsChange:(CGRect)newBounds - (void)updateView { CGSize newSize = [[_bottomSheetController.view.layer.presentationLayer valueForKeyPath:@"frame.size"] CGSizeValue]; - if (!_dragging && !CGSizeEqualToSize(_oldSize, newSize)) { + if (!CGSizeEqualToSize(_oldSize, newSize)) { _oldSize = newSize; [_bridge.uiManager setSize:newSize forView:_reactSubview]; } } -- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished -{ - _dragging = YES; -} - - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex { [super insertReactSubview:subview atIndex:atIndex]; From 26b54076f03638f5bd168f4d15e249b0bf10e326 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 6 Nov 2023 14:30:17 +0000 Subject: [PATCH 10/63] Implemented detent and change props on native --- NavigationReactNative/src/BottomSheet.tsx | 4 +-- .../src/ios/NVBottomSheetManager.m | 3 +++ .../src/ios/NVBottomSheetView.h | 6 ++++- .../src/ios/NVBottomSheetView.m | 25 +++++++++++++++++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/NavigationReactNative/src/BottomSheet.tsx b/NavigationReactNative/src/BottomSheet.tsx index b38fcf0e2a..1fa1314aae 100644 --- a/NavigationReactNative/src/BottomSheet.tsx +++ b/NavigationReactNative/src/BottomSheet.tsx @@ -21,8 +21,8 @@ class BottomSheet extends React.Component { } onDetentChanged({nativeEvent}) { var {eventCount: mostRecentEventCount, detent: nativeDetent} = nativeEvent; - var detents = (UIManager as any).getViewManagerConfig('NVBottomSheet').Constants.Detent - var detent = Object.keys(detents).find(name => detents[name] === nativeDetent) + var detents = (UIManager as any).getViewManagerConfig('NVBottomSheet').Constants?.Detent; + var detent = Platform.OS === 'android'? Object.keys(detents).find(name => detents[name] === nativeDetent) : nativeDetent; this.dragging = !detent if (detent) { this.changeDetent(detent); diff --git a/NavigationReactNative/src/ios/NVBottomSheetManager.m b/NavigationReactNative/src/ios/NVBottomSheetManager.m index fe4b2485b3..0be1902d9d 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetManager.m +++ b/NavigationReactNative/src/ios/NVBottomSheetManager.m @@ -15,4 +15,7 @@ - (UIView *)view { return [[NVBottomSheetView alloc] initWithBridge:self.bridge]; } +RCT_EXPORT_VIEW_PROPERTY(detent, NSString) +RCT_EXPORT_VIEW_PROPERTY(onDetentChanged, RCTDirectEventBlock) + @end diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.h b/NavigationReactNative/src/ios/NVBottomSheetView.h index 13c1145ec9..fb48c37c54 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetView.h @@ -1,7 +1,11 @@ #import #import +#import -@interface NVBottomSheetView : UIView + +@interface NVBottomSheetView : UIView + +@property (nonatomic, copy) RCTDirectEventBlock onDetentChanged; -(id)initWithBridge: (RCTBridge *)bridge; diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 19d0669e97..82ced2f9e5 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -34,6 +34,7 @@ @implementation NVBottomSheetView NVBottomSheetController *_bottomSheetController; UIView *_reactSubview; CGSize _oldSize; + NSInteger _nativeEventCount; } - (id)initWithBridge:(RCTBridge *)bridge @@ -41,6 +42,9 @@ - (id)initWithBridge:(RCTBridge *)bridge if (self = [super init]) { _bridge = bridge; _bottomSheetController = [[NVBottomSheetController alloc] init]; + if (@available(iOS 15.0, *)) { + _bottomSheetController.sheetPresentationController.delegate = self; + } UIView *containerView = [UIView new]; containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; _bottomSheetController.view = containerView; @@ -54,6 +58,15 @@ - (id)initWithBridge:(RCTBridge *)bridge return self; } +- (void)setDetent:(NSString *)detent +{ + if (@available(iOS 15.0, *)) { + [_bottomSheetController.sheetPresentationController animateChanges:^{ + _bottomSheetController.sheetPresentationController.selectedDetentIdentifier = [detent isEqual: @"collapsed"] ? UISheetPresentationControllerDetentIdentifierMedium : UISheetPresentationControllerDetentIdentifierLarge; + }]; + } +} + - (void)notifyForBoundsChange:(CGRect)newBounds { if (_reactSubview) { @@ -100,5 +113,17 @@ - (void)didMoveToWindow } } +- (void)sheetPresentationControllerDidChangeSelectedDetentIdentifier:(UISheetPresentationController *)sheetPresentationController +API_AVAILABLE(ios(15.0)){ + _nativeEventCount++; + if (!!self.onDetentChanged) { + self.onDetentChanged(@{ + @"detent": sheetPresentationController.selectedDetentIdentifier == UISheetPresentationControllerDetentIdentifierMedium ? @"collapsed" : @"expanded", + @"eventCount": @(_nativeEventCount), + }); + } + +} + @end From 6b8558923a8c0cc0f632e3a20db8300d95673678 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 6 Nov 2023 15:41:19 +0000 Subject: [PATCH 11/63] Ignored detent prop if js is lagging behind --- .../src/ios/NVBottomSheetManager.m | 1 + NavigationReactNative/src/ios/NVBottomSheetView.h | 2 ++ NavigationReactNative/src/ios/NVBottomSheetView.m | 13 ++++++++----- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetManager.m b/NavigationReactNative/src/ios/NVBottomSheetManager.m index 0be1902d9d..28f5507a86 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetManager.m +++ b/NavigationReactNative/src/ios/NVBottomSheetManager.m @@ -16,6 +16,7 @@ - (UIView *)view { } RCT_EXPORT_VIEW_PROPERTY(detent, NSString) +RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger) RCT_EXPORT_VIEW_PROPERTY(onDetentChanged, RCTDirectEventBlock) @end diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.h b/NavigationReactNative/src/ios/NVBottomSheetView.h index fb48c37c54..1c30373104 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetView.h @@ -5,6 +5,8 @@ @interface NVBottomSheetView : UIView +@property (nonatomic, copy) NSString *detent; +@property (nonatomic, assign) NSInteger mostRecentEventCount; @property (nonatomic, copy) RCTDirectEventBlock onDetentChanged; -(id)initWithBridge: (RCTBridge *)bridge; diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 82ced2f9e5..be6f408324 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -58,12 +58,16 @@ - (id)initWithBridge:(RCTBridge *)bridge return self; } -- (void)setDetent:(NSString *)detent +- (void)didSetProps:(NSArray *)changedProps { + NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; if (@available(iOS 15.0, *)) { - [_bottomSheetController.sheetPresentationController animateChanges:^{ - _bottomSheetController.sheetPresentationController.selectedDetentIdentifier = [detent isEqual: @"collapsed"] ? UISheetPresentationControllerDetentIdentifierMedium : UISheetPresentationControllerDetentIdentifierLarge; - }]; + UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? UISheetPresentationControllerDetentIdentifierMedium : UISheetPresentationControllerDetentIdentifierLarge; + if (eventLag == 0 && [_bottomSheetController.sheetPresentationController selectedDetentIdentifier] != newDetent) { + [_bottomSheetController.sheetPresentationController animateChanges:^{ + _bottomSheetController.sheetPresentationController.selectedDetentIdentifier = newDetent; + }]; + } } } @@ -122,7 +126,6 @@ - (void)sheetPresentationControllerDidChangeSelectedDetentIdentifier:(UISheetPre @"eventCount": @(_nativeEventCount), }); } - } @end From fac004a121ceb6b048829cb4e62bc88438581a2b Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 6 Nov 2023 18:41:56 +0000 Subject: [PATCH 12/63] Used peekHeight as custom collapsed detent By default the collapsed detent is the built-in medium. But if peekHeight provided then defined a custom collapsed detent with that height and used it in place of the medium detent. This matches the behaviour of peekHeight on android --- .../src/ios/NVBottomSheetManager.m | 7 ++- .../src/ios/NVBottomSheetView.h | 3 ++ .../src/ios/NVBottomSheetView.m | 44 ++++++++++++------- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetManager.m b/NavigationReactNative/src/ios/NVBottomSheetManager.m index 28f5507a86..4f7fb0cae7 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetManager.m +++ b/NavigationReactNative/src/ios/NVBottomSheetManager.m @@ -12,10 +12,15 @@ @implementation NVBottomSheetManager RCT_EXPORT_MODULE() - (UIView *)view { - return [[NVBottomSheetView alloc] initWithBridge:self.bridge]; + if (@available(iOS 15.0, *)) { + return [[NVBottomSheetView alloc] initWithBridge:self.bridge]; + } else { + return nil; + } } RCT_EXPORT_VIEW_PROPERTY(detent, NSString) +RCT_EXPORT_VIEW_PROPERTY(peekHeight, NSInteger) RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger) RCT_EXPORT_VIEW_PROPERTY(onDetentChanged, RCTDirectEventBlock) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.h b/NavigationReactNative/src/ios/NVBottomSheetView.h index 1c30373104..f45b0c6193 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetView.h @@ -3,9 +3,12 @@ #import +API_AVAILABLE(ios(15.0)) +API_AVAILABLE(ios(15.0)) @interface NVBottomSheetView : UIView @property (nonatomic, copy) NSString *detent; +@property (nonatomic, assign) NSInteger peekHeight; @property (nonatomic, assign) NSInteger mostRecentEventCount; @property (nonatomic, copy) RCTDirectEventBlock onDetentChanged; diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index be6f408324..50d074b168 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -35,6 +35,7 @@ @implementation NVBottomSheetView UIView *_reactSubview; CGSize _oldSize; NSInteger _nativeEventCount; + UISheetPresentationControllerDetent *_collapsedDetent; } - (id)initWithBridge:(RCTBridge *)bridge @@ -42,9 +43,8 @@ - (id)initWithBridge:(RCTBridge *)bridge if (self = [super init]) { _bridge = bridge; _bottomSheetController = [[NVBottomSheetController alloc] init]; - if (@available(iOS 15.0, *)) { - _bottomSheetController.sheetPresentationController.delegate = self; - } + _bottomSheetController.sheetPresentationController.delegate = self; + _collapsedDetent = UISheetPresentationControllerDetent.mediumDetent; UIView *containerView = [UIView new]; containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; _bottomSheetController.view = containerView; @@ -60,15 +60,27 @@ - (id)initWithBridge:(RCTBridge *)bridge - (void)didSetProps:(NSArray *)changedProps { - NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; - if (@available(iOS 15.0, *)) { - UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? UISheetPresentationControllerDetentIdentifierMedium : UISheetPresentationControllerDetentIdentifierLarge; - if (eventLag == 0 && [_bottomSheetController.sheetPresentationController selectedDetentIdentifier] != newDetent) { - [_bottomSheetController.sheetPresentationController animateChanges:^{ - _bottomSheetController.sheetPresentationController.selectedDetentIdentifier = newDetent; + UISheetPresentationController *sheet = _bottomSheetController.sheetPresentationController; + _collapsedDetent = UISheetPresentationControllerDetent.mediumDetent; + if (@available(iOS 16.0, *)) { + if (_peekHeight > 0) { + _collapsedDetent = [UISheetPresentationControllerDetent customDetentWithIdentifier:@"collapsed" resolver:^CGFloat(id _Nonnull context) { + return self->_peekHeight; }]; } } + [sheet setDetents:@[_collapsedDetent, UISheetPresentationControllerDetent.largeDetent]]; + NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; + UISheetPresentationControllerDetentIdentifier collapsedIdentifier = UISheetPresentationControllerDetentIdentifierMedium; + if (@available(iOS 16.0, *)) { + collapsedIdentifier = _collapsedDetent.identifier; + } + UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? collapsedIdentifier : UISheetPresentationControllerDetentIdentifierLarge; + if (eventLag == 0 && [sheet selectedDetentIdentifier] != newDetent) { + [sheet animateChanges:^{ + sheet.selectedDetentIdentifier = newDetent; + }]; + } } - (void)notifyForBoundsChange:(CGRect)newBounds @@ -110,19 +122,19 @@ - (void)didUpdateReactSubviews - (void)didMoveToWindow { [super didMoveToWindow]; - if (@available(iOS 15.0, *)) { - UISheetPresentationController *sheet = _bottomSheetController.sheetPresentationController; - [sheet setDetents:@[UISheetPresentationControllerDetent.mediumDetent, UISheetPresentationControllerDetent.largeDetent]]; - [[self reactViewController] presentViewController:_bottomSheetController animated:true completion:nil]; - } + [[self reactViewController] presentViewController:_bottomSheetController animated:true completion:nil]; } - (void)sheetPresentationControllerDidChangeSelectedDetentIdentifier:(UISheetPresentationController *)sheetPresentationController -API_AVAILABLE(ios(15.0)){ +{ _nativeEventCount++; if (!!self.onDetentChanged) { + UISheetPresentationControllerDetentIdentifier collapsedIdentifier = UISheetPresentationControllerDetentIdentifierMedium; + if (@available(iOS 16.0, *)) { + collapsedIdentifier = _collapsedDetent.identifier; + } self.onDetentChanged(@{ - @"detent": sheetPresentationController.selectedDetentIdentifier == UISheetPresentationControllerDetentIdentifierMedium ? @"collapsed" : @"expanded", + @"detent": sheetPresentationController.selectedDetentIdentifier == collapsedIdentifier ? @"collapsed" : @"expanded", @"eventCount": @(_nativeEventCount), }); } From 70b57447899c8a0517c256cecfd8d35300e760ab Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 6 Nov 2023 18:42:12 +0000 Subject: [PATCH 13/63] Removed duplicated line --- NavigationReactNative/src/ios/NVBottomSheetView.h | 1 - 1 file changed, 1 deletion(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.h b/NavigationReactNative/src/ios/NVBottomSheetView.h index f45b0c6193..a9669786ce 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetView.h @@ -3,7 +3,6 @@ #import -API_AVAILABLE(ios(15.0)) API_AVAILABLE(ios(15.0)) @interface NVBottomSheetView : UIView From 6e6aecf8f07cc2ae0bb13caec37861e4127409f4 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 6 Nov 2023 18:46:55 +0000 Subject: [PATCH 14/63] Shared collapsed identifier --- .../src/ios/NVBottomSheetView.m | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 50d074b168..a28ad6b8e7 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -71,11 +71,7 @@ - (void)didSetProps:(NSArray *)changedProps } [sheet setDetents:@[_collapsedDetent, UISheetPresentationControllerDetent.largeDetent]]; NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; - UISheetPresentationControllerDetentIdentifier collapsedIdentifier = UISheetPresentationControllerDetentIdentifierMedium; - if (@available(iOS 16.0, *)) { - collapsedIdentifier = _collapsedDetent.identifier; - } - UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? collapsedIdentifier : UISheetPresentationControllerDetentIdentifierLarge; + UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? [self collapsedIdentifier] : UISheetPresentationControllerDetentIdentifierLarge; if (eventLag == 0 && [sheet selectedDetentIdentifier] != newDetent) { [sheet animateChanges:^{ sheet.selectedDetentIdentifier = newDetent; @@ -83,6 +79,15 @@ - (void)didSetProps:(NSArray *)changedProps } } +- (UISheetPresentationControllerDetentIdentifier) collapsedIdentifier +{ + UISheetPresentationControllerDetentIdentifier collapsedIdentifier = UISheetPresentationControllerDetentIdentifierMedium; + if (@available(iOS 16.0, *)) { + collapsedIdentifier = _collapsedDetent.identifier; + } + return collapsedIdentifier; +} + - (void)notifyForBoundsChange:(CGRect)newBounds { if (_reactSubview) { @@ -129,12 +134,8 @@ - (void)sheetPresentationControllerDidChangeSelectedDetentIdentifier:(UISheetPre { _nativeEventCount++; if (!!self.onDetentChanged) { - UISheetPresentationControllerDetentIdentifier collapsedIdentifier = UISheetPresentationControllerDetentIdentifierMedium; - if (@available(iOS 16.0, *)) { - collapsedIdentifier = _collapsedDetent.identifier; - } self.onDetentChanged(@{ - @"detent": sheetPresentationController.selectedDetentIdentifier == collapsedIdentifier ? @"collapsed" : @"expanded", + @"detent": sheetPresentationController.selectedDetentIdentifier == [self collapsedIdentifier] ? @"collapsed" : @"expanded", @"eventCount": @(_nativeEventCount), }); } From dd6c728c9b1f5dd89325086ebd3eb79765201af3 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 6 Nov 2023 21:08:46 +0000 Subject: [PATCH 15/63] Animated change in peekHeight Tested dynamic peekHeight and ios automatically resizes --- NavigationReactNative/src/ios/NVBottomSheetView.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index a28ad6b8e7..f3120d6a26 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -69,14 +69,14 @@ - (void)didSetProps:(NSArray *)changedProps }]; } } - [sheet setDetents:@[_collapsedDetent, UISheetPresentationControllerDetent.largeDetent]]; NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? [self collapsedIdentifier] : UISheetPresentationControllerDetentIdentifierLarge; - if (eventLag == 0 && [sheet selectedDetentIdentifier] != newDetent) { - [sheet animateChanges:^{ + [sheet animateChanges:^{ + [sheet setDetents:@[_collapsedDetent, UISheetPresentationControllerDetent.largeDetent]]; + if (eventLag == 0 && [sheet selectedDetentIdentifier] != newDetent) { sheet.selectedDetentIdentifier = newDetent; - }]; - } + } + }]; } - (UISheetPresentationControllerDetentIdentifier) collapsedIdentifier From a1eec966c93546db3c29836f1359583227e4bc29 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 6 Nov 2023 21:10:55 +0000 Subject: [PATCH 16/63] Used optional chaining for simplicity Keeps diff to a minimum --- NavigationReactNative/src/BottomSheet.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NavigationReactNative/src/BottomSheet.tsx b/NavigationReactNative/src/BottomSheet.tsx index 1fa1314aae..3f5faaa4be 100644 --- a/NavigationReactNative/src/BottomSheet.tsx +++ b/NavigationReactNative/src/BottomSheet.tsx @@ -41,11 +41,11 @@ class BottomSheet extends React.Component { render() { var { expandedHeight, expandedOffset, peekHeight, halfExpandedRatio, hideable, skipCollapsed, draggable, children } = this.props var { selectedDetent } = this.state; - const constants = (UIManager as any).getViewManagerConfig('NVBottomSheet').Constants; + const detents = (UIManager as any).getViewManagerConfig('NVBottomSheet').Constants?.Detent; return ( Date: Mon, 6 Nov 2023 23:28:11 +0000 Subject: [PATCH 17/63] Implemented expanded height and offset props Used them to set a custom detent that acts as the large detent. The expanded height is the detent height and the offset is taken off the max detent height --- NavigationReactNative/src/BottomSheet.tsx | 8 ++----- .../src/ios/NVBottomSheetManager.m | 2 ++ .../src/ios/NVBottomSheetView.h | 2 ++ .../src/ios/NVBottomSheetView.m | 21 +++++++++++++++++-- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/NavigationReactNative/src/BottomSheet.tsx b/NavigationReactNative/src/BottomSheet.tsx index 3f5faaa4be..2a67634328 100644 --- a/NavigationReactNative/src/BottomSheet.tsx +++ b/NavigationReactNative/src/BottomSheet.tsx @@ -47,6 +47,7 @@ class BottomSheet extends React.Component { ref={this.ref} detent={Platform.OS === 'android' ? detents[this.state.selectedDetent] : selectedDetent} peekHeight={peekHeight} + expandedHeight={expandedHeight} expandedOffset={expandedOffset} fitToContents={expandedOffset == null} halfExpandedRatio={halfExpandedRatio} @@ -57,12 +58,7 @@ class BottomSheet extends React.Component { mostRecentEventCount={this.state.mostRecentEventCount} onMoveShouldSetResponderCapture={() => this.dragging} onDetentChanged={this.onDetentChanged} - style={[ - styles.bottomSheet, - expandedHeight != null ? { height: expandedHeight } : null, - expandedOffset != null ? { top: expandedOffset } : null, - expandedHeight == null && expandedOffset == null ? { top: undefined } : null - ]} + style={styles.bottomSheet} > {children} diff --git a/NavigationReactNative/src/ios/NVBottomSheetManager.m b/NavigationReactNative/src/ios/NVBottomSheetManager.m index 4f7fb0cae7..ac73f13d69 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetManager.m +++ b/NavigationReactNative/src/ios/NVBottomSheetManager.m @@ -21,6 +21,8 @@ - (UIView *)view { RCT_EXPORT_VIEW_PROPERTY(detent, NSString) RCT_EXPORT_VIEW_PROPERTY(peekHeight, NSInteger) +RCT_EXPORT_VIEW_PROPERTY(expandedHeight, NSInteger) +RCT_EXPORT_VIEW_PROPERTY(expandedOffset, NSInteger) RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger) RCT_EXPORT_VIEW_PROPERTY(onDetentChanged, RCTDirectEventBlock) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.h b/NavigationReactNative/src/ios/NVBottomSheetView.h index a9669786ce..2e4aa246d1 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetView.h @@ -8,6 +8,8 @@ API_AVAILABLE(ios(15.0)) @property (nonatomic, copy) NSString *detent; @property (nonatomic, assign) NSInteger peekHeight; +@property (nonatomic, assign) NSInteger expandedHeight; +@property (nonatomic, assign) NSInteger expandedOffset; @property (nonatomic, assign) NSInteger mostRecentEventCount; @property (nonatomic, copy) RCTDirectEventBlock onDetentChanged; diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index f3120d6a26..e7153e7481 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -36,6 +36,7 @@ @implementation NVBottomSheetView CGSize _oldSize; NSInteger _nativeEventCount; UISheetPresentationControllerDetent *_collapsedDetent; + UISheetPresentationControllerDetent *_expandedDetent; } - (id)initWithBridge:(RCTBridge *)bridge @@ -45,6 +46,7 @@ - (id)initWithBridge:(RCTBridge *)bridge _bottomSheetController = [[NVBottomSheetController alloc] init]; _bottomSheetController.sheetPresentationController.delegate = self; _collapsedDetent = UISheetPresentationControllerDetent.mediumDetent; + _expandedDetent = UISheetPresentationControllerDetent.largeDetent; UIView *containerView = [UIView new]; containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; _bottomSheetController.view = containerView; @@ -62,17 +64,23 @@ - (void)didSetProps:(NSArray *)changedProps { UISheetPresentationController *sheet = _bottomSheetController.sheetPresentationController; _collapsedDetent = UISheetPresentationControllerDetent.mediumDetent; + _expandedDetent = UISheetPresentationControllerDetent.largeDetent; if (@available(iOS 16.0, *)) { if (_peekHeight > 0) { _collapsedDetent = [UISheetPresentationControllerDetent customDetentWithIdentifier:@"collapsed" resolver:^CGFloat(id _Nonnull context) { return self->_peekHeight; }]; } + if (_expandedHeight > 0 || _expandedOffset > 0) { + _expandedDetent = [UISheetPresentationControllerDetent customDetentWithIdentifier:@"expanded" resolver:^CGFloat(id _Nonnull context) { + return self->_expandedHeight > 0 ? self->_expandedHeight : context.maximumDetentValue - self->_expandedOffset; + }]; + } } NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; - UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? [self collapsedIdentifier] : UISheetPresentationControllerDetentIdentifierLarge; + UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? [self collapsedIdentifier] : [self expandedIdentifier]; [sheet animateChanges:^{ - [sheet setDetents:@[_collapsedDetent, UISheetPresentationControllerDetent.largeDetent]]; + [sheet setDetents:@[_collapsedDetent, _expandedDetent]]; if (eventLag == 0 && [sheet selectedDetentIdentifier] != newDetent) { sheet.selectedDetentIdentifier = newDetent; } @@ -88,6 +96,15 @@ - (UISheetPresentationControllerDetentIdentifier) collapsedIdentifier return collapsedIdentifier; } +- (UISheetPresentationControllerDetentIdentifier) expandedIdentifier +{ + UISheetPresentationControllerDetentIdentifier expandedIdentifier = UISheetPresentationControllerDetentIdentifierLarge; + if (@available(iOS 16.0, *)) { + expandedIdentifier = _expandedDetent.identifier; + } + return expandedIdentifier; +} + - (void)notifyForBoundsChange:(CGRect)newBounds { if (_reactSubview) { From a9c35045b755d934af1ec2b8c331342c9f5341ed Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 6 Nov 2023 23:32:19 +0000 Subject: [PATCH 18/63] Cleared height and top on ios for simplicity These styles only apply on android --- NavigationReactNative/src/BottomSheet.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/NavigationReactNative/src/BottomSheet.tsx b/NavigationReactNative/src/BottomSheet.tsx index 2a67634328..5ceb9e543f 100644 --- a/NavigationReactNative/src/BottomSheet.tsx +++ b/NavigationReactNative/src/BottomSheet.tsx @@ -58,7 +58,13 @@ class BottomSheet extends React.Component { mostRecentEventCount={this.state.mostRecentEventCount} onMoveShouldSetResponderCapture={() => this.dragging} onDetentChanged={this.onDetentChanged} - style={styles.bottomSheet} + style={[ + styles.bottomSheet, + expandedHeight != null ? { height: expandedHeight } : null, + expandedOffset != null ? { top: expandedOffset } : null, + expandedHeight == null && expandedOffset == null ? { top: 0 } : null, + Platform.OS === 'ios' ? { height: undefined, top: undefined } : null, + ]} > {children} From 8b973c8611e80c02af9f2a3760a9639ffa76f0b8 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 6 Nov 2023 23:41:46 +0000 Subject: [PATCH 19/63] Returned null if bottom sheet not supported UISheetPresentationController not available in iOS < 15 --- NavigationReactNative/src/BottomSheet.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/NavigationReactNative/src/BottomSheet.tsx b/NavigationReactNative/src/BottomSheet.tsx index 5ceb9e543f..7ade64d6ba 100644 --- a/NavigationReactNative/src/BottomSheet.tsx +++ b/NavigationReactNative/src/BottomSheet.tsx @@ -39,6 +39,7 @@ class BottomSheet extends React.Component { } } render() { + if (Platform.OS === 'ios' && +Platform.Version < 15) return null; var { expandedHeight, expandedOffset, peekHeight, halfExpandedRatio, hideable, skipCollapsed, draggable, children } = this.props var { selectedDetent } = this.state; const detents = (UIManager as any).getViewManagerConfig('NVBottomSheet').Constants?.Detent; From b585507c88c49f393ddefc61c68379e630c7428f Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Tue, 7 Nov 2023 09:34:36 +0000 Subject: [PATCH 20/63] Implemented halfExpandedRatio prop Followed same pattern as done for collapsed and expanded detents. Created a custom detent and defaulted it to large. Only added if not the default --- .../src/ios/NVBottomSheetManager.m | 1 + .../src/ios/NVBottomSheetView.h | 1 + .../src/ios/NVBottomSheetView.m | 24 ++++++++++++++++--- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetManager.m b/NavigationReactNative/src/ios/NVBottomSheetManager.m index ac73f13d69..94d26a283e 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetManager.m +++ b/NavigationReactNative/src/ios/NVBottomSheetManager.m @@ -23,6 +23,7 @@ - (UIView *)view { RCT_EXPORT_VIEW_PROPERTY(peekHeight, NSInteger) RCT_EXPORT_VIEW_PROPERTY(expandedHeight, NSInteger) RCT_EXPORT_VIEW_PROPERTY(expandedOffset, NSInteger) +RCT_EXPORT_VIEW_PROPERTY(halfExpandedRatio, double) RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger) RCT_EXPORT_VIEW_PROPERTY(onDetentChanged, RCTDirectEventBlock) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.h b/NavigationReactNative/src/ios/NVBottomSheetView.h index 2e4aa246d1..c74ccc1c83 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetView.h @@ -10,6 +10,7 @@ API_AVAILABLE(ios(15.0)) @property (nonatomic, assign) NSInteger peekHeight; @property (nonatomic, assign) NSInteger expandedHeight; @property (nonatomic, assign) NSInteger expandedOffset; +@property (nonatomic, assign) double halfExpandedRatio; @property (nonatomic, assign) NSInteger mostRecentEventCount; @property (nonatomic, copy) RCTDirectEventBlock onDetentChanged; diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index e7153e7481..79fce248bc 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -37,6 +37,7 @@ @implementation NVBottomSheetView NSInteger _nativeEventCount; UISheetPresentationControllerDetent *_collapsedDetent; UISheetPresentationControllerDetent *_expandedDetent; + UISheetPresentationControllerDetent *_halfExpandedDetent; } - (id)initWithBridge:(RCTBridge *)bridge @@ -47,6 +48,7 @@ - (id)initWithBridge:(RCTBridge *)bridge _bottomSheetController.sheetPresentationController.delegate = self; _collapsedDetent = UISheetPresentationControllerDetent.mediumDetent; _expandedDetent = UISheetPresentationControllerDetent.largeDetent; + _halfExpandedDetent = UISheetPresentationControllerDetent.largeDetent; UIView *containerView = [UIView new]; containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; _bottomSheetController.view = containerView; @@ -65,6 +67,7 @@ - (void)didSetProps:(NSArray *)changedProps UISheetPresentationController *sheet = _bottomSheetController.sheetPresentationController; _collapsedDetent = UISheetPresentationControllerDetent.mediumDetent; _expandedDetent = UISheetPresentationControllerDetent.largeDetent; + _halfExpandedDetent = UISheetPresentationControllerDetent.largeDetent; if (@available(iOS 16.0, *)) { if (_peekHeight > 0) { _collapsedDetent = [UISheetPresentationControllerDetent customDetentWithIdentifier:@"collapsed" resolver:^CGFloat(id _Nonnull context) { @@ -76,11 +79,17 @@ - (void)didSetProps:(NSArray *)changedProps return self->_expandedHeight > 0 ? self->_expandedHeight : context.maximumDetentValue - self->_expandedOffset; }]; } + + if (_halfExpandedRatio > 0) { + _halfExpandedDetent = [UISheetPresentationControllerDetent customDetentWithIdentifier:@"halfExpanded" resolver:^CGFloat(id _Nonnull context) { + return self-> _halfExpandedRatio * context.maximumDetentValue; + }]; + } } NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; - UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? [self collapsedIdentifier] : [self expandedIdentifier]; + UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? [self collapsedIdentifier] : ([_detent isEqual: @"expanded"] ? [self expandedIdentifier] : [self halfExpandedIdentifier]); [sheet animateChanges:^{ - [sheet setDetents:@[_collapsedDetent, _expandedDetent]]; + [sheet setDetents: _halfExpandedDetent == UISheetPresentationControllerDetent.largeDetent ? @[_collapsedDetent, _expandedDetent] : @[_collapsedDetent, _halfExpandedDetent, _expandedDetent]]; if (eventLag == 0 && [sheet selectedDetentIdentifier] != newDetent) { sheet.selectedDetentIdentifier = newDetent; } @@ -96,6 +105,15 @@ - (UISheetPresentationControllerDetentIdentifier) collapsedIdentifier return collapsedIdentifier; } +- (UISheetPresentationControllerDetentIdentifier) halfExpandedIdentifier +{ + UISheetPresentationControllerDetentIdentifier halfExpandedIdentifier = UISheetPresentationControllerDetentIdentifierLarge; + if (@available(iOS 16.0, *)) { + halfExpandedIdentifier = _halfExpandedDetent.identifier; + } + return halfExpandedIdentifier; +} + - (UISheetPresentationControllerDetentIdentifier) expandedIdentifier { UISheetPresentationControllerDetentIdentifier expandedIdentifier = UISheetPresentationControllerDetentIdentifierLarge; @@ -152,7 +170,7 @@ - (void)sheetPresentationControllerDidChangeSelectedDetentIdentifier:(UISheetPre _nativeEventCount++; if (!!self.onDetentChanged) { self.onDetentChanged(@{ - @"detent": sheetPresentationController.selectedDetentIdentifier == [self collapsedIdentifier] ? @"collapsed" : @"expanded", + @"detent": sheetPresentationController.selectedDetentIdentifier == [self collapsedIdentifier] ? @"collapsed" : (sheetPresentationController.selectedDetentIdentifier == [self expandedIdentifier] ? @"expanded" : @"halfExpanded"), @"eventCount": @(_nativeEventCount), }); } From 6b79c4fe3693d9ec2b163ced689d353c2df0bd00 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Tue, 7 Nov 2023 10:53:26 +0000 Subject: [PATCH 21/63] Compared identifiers because detents not equal --- NavigationReactNative/src/ios/NVBottomSheetView.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 79fce248bc..d300ed3570 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -89,7 +89,7 @@ - (void)didSetProps:(NSArray *)changedProps NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? [self collapsedIdentifier] : ([_detent isEqual: @"expanded"] ? [self expandedIdentifier] : [self halfExpandedIdentifier]); [sheet animateChanges:^{ - [sheet setDetents: _halfExpandedDetent == UISheetPresentationControllerDetent.largeDetent ? @[_collapsedDetent, _expandedDetent] : @[_collapsedDetent, _halfExpandedDetent, _expandedDetent]]; + [sheet setDetents: [self halfExpandedIdentifier] == UISheetPresentationControllerDetentIdentifierLarge ? @[_collapsedDetent, _expandedDetent] : @[_collapsedDetent, _halfExpandedDetent, _expandedDetent]]; if (eventLag == 0 && [sheet selectedDetentIdentifier] != newDetent) { sheet.selectedDetentIdentifier = newDetent; } From b7ced0410bfe613d660e0c8c658f9fca9ad524c5 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Tue, 7 Nov 2023 10:57:01 +0000 Subject: [PATCH 22/63] Compared with isEqual for safety --- NavigationReactNative/src/ios/NVBottomSheetView.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index d300ed3570..a9694ff3cb 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -89,7 +89,7 @@ - (void)didSetProps:(NSArray *)changedProps NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? [self collapsedIdentifier] : ([_detent isEqual: @"expanded"] ? [self expandedIdentifier] : [self halfExpandedIdentifier]); [sheet animateChanges:^{ - [sheet setDetents: [self halfExpandedIdentifier] == UISheetPresentationControllerDetentIdentifierLarge ? @[_collapsedDetent, _expandedDetent] : @[_collapsedDetent, _halfExpandedDetent, _expandedDetent]]; + [sheet setDetents: [[self halfExpandedIdentifier] isEqual:UISheetPresentationControllerDetentIdentifierLarge] ? @[_collapsedDetent, _expandedDetent] : @[_collapsedDetent, _halfExpandedDetent, _expandedDetent]]; if (eventLag == 0 && [sheet selectedDetentIdentifier] != newDetent) { sheet.selectedDetentIdentifier = newDetent; } @@ -169,8 +169,9 @@ - (void)sheetPresentationControllerDidChangeSelectedDetentIdentifier:(UISheetPre { _nativeEventCount++; if (!!self.onDetentChanged) { + UISheetPresentationControllerDetentIdentifier detentId = sheetPresentationController.selectedDetentIdentifier; self.onDetentChanged(@{ - @"detent": sheetPresentationController.selectedDetentIdentifier == [self collapsedIdentifier] ? @"collapsed" : (sheetPresentationController.selectedDetentIdentifier == [self expandedIdentifier] ? @"expanded" : @"halfExpanded"), + @"detent": [[self collapsedIdentifier] isEqual:detentId] ? @"collapsed" : ([[self expandedIdentifier] isEqual:detentId] ? @"expanded" : @"halfExpanded"), @"eventCount": @(_nativeEventCount), }); } From 7568ae9c8e3a797852932507e433152000000a45 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Tue, 7 Nov 2023 11:08:41 +0000 Subject: [PATCH 23/63] Implemented hideable prop Prevented swipe to dismiss if not hideable --- NavigationReactNative/src/ios/NVBottomSheetManager.m | 1 + NavigationReactNative/src/ios/NVBottomSheetView.h | 3 ++- NavigationReactNative/src/ios/NVBottomSheetView.m | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetManager.m b/NavigationReactNative/src/ios/NVBottomSheetManager.m index 94d26a283e..99c6ae9b5a 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetManager.m +++ b/NavigationReactNative/src/ios/NVBottomSheetManager.m @@ -24,6 +24,7 @@ - (UIView *)view { RCT_EXPORT_VIEW_PROPERTY(expandedHeight, NSInteger) RCT_EXPORT_VIEW_PROPERTY(expandedOffset, NSInteger) RCT_EXPORT_VIEW_PROPERTY(halfExpandedRatio, double) +RCT_EXPORT_VIEW_PROPERTY(hideable, BOOL) RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger) RCT_EXPORT_VIEW_PROPERTY(onDetentChanged, RCTDirectEventBlock) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.h b/NavigationReactNative/src/ios/NVBottomSheetView.h index c74ccc1c83..dd1fce85cd 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetView.h @@ -4,13 +4,14 @@ API_AVAILABLE(ios(15.0)) -@interface NVBottomSheetView : UIView +@interface NVBottomSheetView : UIView @property (nonatomic, copy) NSString *detent; @property (nonatomic, assign) NSInteger peekHeight; @property (nonatomic, assign) NSInteger expandedHeight; @property (nonatomic, assign) NSInteger expandedOffset; @property (nonatomic, assign) double halfExpandedRatio; +@property (nonatomic, assign) BOOL hideable; @property (nonatomic, assign) NSInteger mostRecentEventCount; @property (nonatomic, copy) RCTDirectEventBlock onDetentChanged; diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index a9694ff3cb..0220c6b2fa 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -46,6 +46,7 @@ - (id)initWithBridge:(RCTBridge *)bridge _bridge = bridge; _bottomSheetController = [[NVBottomSheetController alloc] init]; _bottomSheetController.sheetPresentationController.delegate = self; + _bottomSheetController.presentationController.delegate = self; _collapsedDetent = UISheetPresentationControllerDetent.mediumDetent; _expandedDetent = UISheetPresentationControllerDetent.largeDetent; _halfExpandedDetent = UISheetPresentationControllerDetent.largeDetent; @@ -177,5 +178,10 @@ - (void)sheetPresentationControllerDidChangeSelectedDetentIdentifier:(UISheetPre } } +- (BOOL)presentationControllerShouldDismiss:(UIPresentationController *)presentationController +{ + return _hideable; +} + @end From a44211947bc0365836fe5ca9367e2bbe415b75d9 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Tue, 7 Nov 2023 15:43:21 +0000 Subject: [PATCH 24/63] Implemented hidden 'detent' --- .../src/ios/NVBottomSheetView.m | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 0220c6b2fa..a1999e3e6e 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -89,12 +89,19 @@ - (void)didSetProps:(NSArray *)changedProps } NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? [self collapsedIdentifier] : ([_detent isEqual: @"expanded"] ? [self expandedIdentifier] : [self halfExpandedIdentifier]); - [sheet animateChanges:^{ - [sheet setDetents: [[self halfExpandedIdentifier] isEqual:UISheetPresentationControllerDetentIdentifierLarge] ? @[_collapsedDetent, _expandedDetent] : @[_collapsedDetent, _halfExpandedDetent, _expandedDetent]]; - if (eventLag == 0 && [sheet selectedDetentIdentifier] != newDetent) { - sheet.selectedDetentIdentifier = newDetent; + if (![_detent isEqual: @"hidden"]) { + if (![_bottomSheetController isBeingPresented] && self.window) { + [[self reactViewController] presentViewController:_bottomSheetController animated:true completion:nil]; } - }]; + [sheet animateChanges:^{ + [sheet setDetents: [[self halfExpandedIdentifier] isEqual:UISheetPresentationControllerDetentIdentifierLarge] ? @[_collapsedDetent, _expandedDetent] : @[_collapsedDetent, _halfExpandedDetent, _expandedDetent]]; + if (eventLag == 0 && [sheet selectedDetentIdentifier] != newDetent) { + sheet.selectedDetentIdentifier = newDetent; + } + }]; + } else { + [_bottomSheetController dismissViewControllerAnimated:YES completion:nil]; + } } - (UISheetPresentationControllerDetentIdentifier) collapsedIdentifier @@ -163,7 +170,9 @@ - (void)didUpdateReactSubviews - (void)didMoveToWindow { [super didMoveToWindow]; - [[self reactViewController] presentViewController:_bottomSheetController animated:true completion:nil]; + if (![_detent isEqual: @"hidden"] &&![_bottomSheetController isBeingPresented]) { + [[self reactViewController] presentViewController:_bottomSheetController animated:true completion:nil]; + } } - (void)sheetPresentationControllerDidChangeSelectedDetentIdentifier:(UISheetPresentationController *)sheetPresentationController From ac4bf5e8e0354cea94ac4416e8b67fe15154ef85 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Tue, 7 Nov 2023 15:52:06 +0000 Subject: [PATCH 25/63] Fired event when detent changed to hidden --- NavigationReactNative/src/ios/NVBottomSheetView.m | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index a1999e3e6e..02bc121cb5 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -90,7 +90,7 @@ - (void)didSetProps:(NSArray *)changedProps NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? [self collapsedIdentifier] : ([_detent isEqual: @"expanded"] ? [self expandedIdentifier] : [self halfExpandedIdentifier]); if (![_detent isEqual: @"hidden"]) { - if (![_bottomSheetController isBeingPresented] && self.window) { + if (!_reactSubview.window && self.window) { [[self reactViewController] presentViewController:_bottomSheetController animated:true completion:nil]; } [sheet animateChanges:^{ @@ -170,7 +170,7 @@ - (void)didUpdateReactSubviews - (void)didMoveToWindow { [super didMoveToWindow]; - if (![_detent isEqual: @"hidden"] &&![_bottomSheetController isBeingPresented]) { + if (![_detent isEqual: @"hidden"] &&!_reactSubview.window) { [[self reactViewController] presentViewController:_bottomSheetController animated:true completion:nil]; } } @@ -192,5 +192,16 @@ - (BOOL)presentationControllerShouldDismiss:(UIPresentationController *)presenta return _hideable; } +- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController +{ + _nativeEventCount++; + if (!!self.onDetentChanged) { + self.onDetentChanged(@{ + @"detent": @"hidden", + @"eventCount": @(_nativeEventCount), + }); + } +} + @end From 42a012d875964f473ddefad32225ccf8dda975d1 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Tue, 7 Nov 2023 16:12:00 +0000 Subject: [PATCH 26/63] Implemented draggable prop If not draggable added only the selected detent. So can't drag because only valid detent is the selected one. Checked can still programmatically change to another detent --- NavigationReactNative/src/ios/NVBottomSheetManager.m | 1 + NavigationReactNative/src/ios/NVBottomSheetView.h | 1 + NavigationReactNative/src/ios/NVBottomSheetView.m | 3 +++ 3 files changed, 5 insertions(+) diff --git a/NavigationReactNative/src/ios/NVBottomSheetManager.m b/NavigationReactNative/src/ios/NVBottomSheetManager.m index 99c6ae9b5a..4e06b8a605 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetManager.m +++ b/NavigationReactNative/src/ios/NVBottomSheetManager.m @@ -25,6 +25,7 @@ - (UIView *)view { RCT_EXPORT_VIEW_PROPERTY(expandedOffset, NSInteger) RCT_EXPORT_VIEW_PROPERTY(halfExpandedRatio, double) RCT_EXPORT_VIEW_PROPERTY(hideable, BOOL) +RCT_EXPORT_VIEW_PROPERTY(draggable, BOOL) RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger) RCT_EXPORT_VIEW_PROPERTY(onDetentChanged, RCTDirectEventBlock) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.h b/NavigationReactNative/src/ios/NVBottomSheetView.h index dd1fce85cd..2b8b936ae5 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetView.h @@ -12,6 +12,7 @@ API_AVAILABLE(ios(15.0)) @property (nonatomic, assign) NSInteger expandedOffset; @property (nonatomic, assign) double halfExpandedRatio; @property (nonatomic, assign) BOOL hideable; +@property (nonatomic, assign) BOOL draggable; @property (nonatomic, assign) NSInteger mostRecentEventCount; @property (nonatomic, copy) RCTDirectEventBlock onDetentChanged; diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 02bc121cb5..6da475e35d 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -98,6 +98,9 @@ - (void)didSetProps:(NSArray *)changedProps if (eventLag == 0 && [sheet selectedDetentIdentifier] != newDetent) { sheet.selectedDetentIdentifier = newDetent; } + if (!_draggable) { + [sheet setDetents: @[[newDetent isEqual:[self collapsedIdentifier]] ? _collapsedDetent : ([newDetent isEqual:[self expandedIdentifier]] ? _expandedDetent : _halfExpandedDetent)]]; + } }]; } else { [_bottomSheetController dismissViewControllerAnimated:YES completion:nil]; From 7edabc76f5cd64102be1f767f9bdac5316ec87d6 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Tue, 7 Nov 2023 16:13:48 +0000 Subject: [PATCH 27/63] Swapped around for clarity Puts all set detents together --- NavigationReactNative/src/ios/NVBottomSheetView.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 6da475e35d..22e0a6998d 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -95,12 +95,12 @@ - (void)didSetProps:(NSArray *)changedProps } [sheet animateChanges:^{ [sheet setDetents: [[self halfExpandedIdentifier] isEqual:UISheetPresentationControllerDetentIdentifierLarge] ? @[_collapsedDetent, _expandedDetent] : @[_collapsedDetent, _halfExpandedDetent, _expandedDetent]]; - if (eventLag == 0 && [sheet selectedDetentIdentifier] != newDetent) { - sheet.selectedDetentIdentifier = newDetent; - } if (!_draggable) { [sheet setDetents: @[[newDetent isEqual:[self collapsedIdentifier]] ? _collapsedDetent : ([newDetent isEqual:[self expandedIdentifier]] ? _expandedDetent : _halfExpandedDetent)]]; } + if (eventLag == 0 && [sheet selectedDetentIdentifier] != newDetent) { + sheet.selectedDetentIdentifier = newDetent; + } }]; } else { [_bottomSheetController dismissViewControllerAnimated:YES completion:nil]; From 4e4074700ee91e2d0e6f6d04688f84ae2060c7b0 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Tue, 7 Nov 2023 16:47:30 +0000 Subject: [PATCH 28/63] Invalidated and closed when removed Also added a presented bool check because the window wasn't good enough. When navigating to another scene the didSetProps fired after didMoveToWindow but the reactSubView window wasn't set so tried to present twice and got error --- .../src/ios/NVBottomSheetView.h | 4 ++-- .../src/ios/NVBottomSheetView.m | 21 ++++++++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.h b/NavigationReactNative/src/ios/NVBottomSheetView.h index 2b8b936ae5..be5ca52b56 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetView.h @@ -1,10 +1,10 @@ #import #import #import - +#import API_AVAILABLE(ios(15.0)) -@interface NVBottomSheetView : UIView +@interface NVBottomSheetView : UIView @property (nonatomic, copy) NSString *detent; @property (nonatomic, assign) NSInteger peekHeight; diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 22e0a6998d..8160b67edf 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -35,9 +35,11 @@ @implementation NVBottomSheetView UIView *_reactSubview; CGSize _oldSize; NSInteger _nativeEventCount; + CADisplayLink *_displayLink; UISheetPresentationControllerDetent *_collapsedDetent; UISheetPresentationControllerDetent *_expandedDetent; UISheetPresentationControllerDetent *_halfExpandedDetent; + BOOL _presented; } - (id)initWithBridge:(RCTBridge *)bridge @@ -53,8 +55,8 @@ - (id)initWithBridge:(RCTBridge *)bridge UIView *containerView = [UIView new]; containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; _bottomSheetController.view = containerView; - CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateView)]; - [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateView)]; + [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; __weak typeof(self) weakSelf = self; _bottomSheetController.boundsDidChangeBlock = ^(CGRect newBounds) { [weakSelf notifyForBoundsChange:newBounds]; @@ -90,7 +92,8 @@ - (void)didSetProps:(NSArray *)changedProps NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? [self collapsedIdentifier] : ([_detent isEqual: @"expanded"] ? [self expandedIdentifier] : [self halfExpandedIdentifier]); if (![_detent isEqual: @"hidden"]) { - if (!_reactSubview.window && self.window) { + if (self.window && !_presented) { + _presented = YES; [[self reactViewController] presentViewController:_bottomSheetController animated:true completion:nil]; } [sheet animateChanges:^{ @@ -152,7 +155,6 @@ - (void)updateView { } } - - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex { [super insertReactSubview:subview atIndex:atIndex]; @@ -170,10 +172,19 @@ - (void)didUpdateReactSubviews { } +-(void)invalidate +{ + [_displayLink invalidate]; + dispatch_async(dispatch_get_main_queue(), ^{ + [self->_bottomSheetController dismissViewControllerAnimated:YES completion:nil]; + }); +} + - (void)didMoveToWindow { [super didMoveToWindow]; - if (![_detent isEqual: @"hidden"] &&!_reactSubview.window) { + if (![_detent isEqual: @"hidden"] && !_presented) { + _presented = YES; [[self reactViewController] presentViewController:_bottomSheetController animated:true completion:nil]; } } From 71640a7be5b51a15fbea5002da7ce66c6045224c Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Tue, 7 Nov 2023 16:52:48 +0000 Subject: [PATCH 29/63] Tweaked format --- NavigationReactNative/src/ios/NVBottomSheetView.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 8160b67edf..c71d772567 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -34,12 +34,12 @@ @implementation NVBottomSheetView NVBottomSheetController *_bottomSheetController; UIView *_reactSubview; CGSize _oldSize; + BOOL _presented; NSInteger _nativeEventCount; CADisplayLink *_displayLink; UISheetPresentationControllerDetent *_collapsedDetent; UISheetPresentationControllerDetent *_expandedDetent; UISheetPresentationControllerDetent *_halfExpandedDetent; - BOOL _presented; } - (id)initWithBridge:(RCTBridge *)bridge From cd117b8ad3195096ce7c29e5813fa6180d36f713 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Tue, 7 Nov 2023 16:58:17 +0000 Subject: [PATCH 30/63] Removed dismissing - not sure it's needed Copied it from the react native modal but would rather wait to find it if it's needed. Maybe if background is undimmed so can close the scene without first dismissing? --- NavigationReactNative/src/ios/NVBottomSheetView.m | 3 --- 1 file changed, 3 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index c71d772567..4528ee7fc3 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -175,9 +175,6 @@ - (void)didUpdateReactSubviews -(void)invalidate { [_displayLink invalidate]; - dispatch_async(dispatch_get_main_queue(), ^{ - [self->_bottomSheetController dismissViewControllerAnimated:YES completion:nil]; - }); } - (void)didMoveToWindow From e236bc50c16d4f234d9701a0f909ae8693a5898d Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Wed, 8 Nov 2023 10:11:16 +0000 Subject: [PATCH 31/63] Allowed bottom sheet to be reopened Reattached delegates because they're gone when the controller is dismissed --- NavigationReactNative/src/ios/NVBottomSheetView.m | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 4528ee7fc3..43b2b85527 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -47,8 +47,6 @@ - (id)initWithBridge:(RCTBridge *)bridge if (self = [super init]) { _bridge = bridge; _bottomSheetController = [[NVBottomSheetController alloc] init]; - _bottomSheetController.sheetPresentationController.delegate = self; - _bottomSheetController.presentationController.delegate = self; _collapsedDetent = UISheetPresentationControllerDetent.mediumDetent; _expandedDetent = UISheetPresentationControllerDetent.largeDetent; _halfExpandedDetent = UISheetPresentationControllerDetent.largeDetent; @@ -94,6 +92,8 @@ - (void)didSetProps:(NSArray *)changedProps if (![_detent isEqual: @"hidden"]) { if (self.window && !_presented) { _presented = YES; + _bottomSheetController.sheetPresentationController.delegate = self; + _bottomSheetController.presentationController.delegate = self; [[self reactViewController] presentViewController:_bottomSheetController animated:true completion:nil]; } [sheet animateChanges:^{ @@ -107,6 +107,7 @@ - (void)didSetProps:(NSArray *)changedProps }]; } else { [_bottomSheetController dismissViewControllerAnimated:YES completion:nil]; + _presented = NO; } } @@ -182,6 +183,8 @@ - (void)didMoveToWindow [super didMoveToWindow]; if (![_detent isEqual: @"hidden"] && !_presented) { _presented = YES; + _bottomSheetController.sheetPresentationController.delegate = self; + _bottomSheetController.presentationController.delegate = self; [[self reactViewController] presentViewController:_bottomSheetController animated:true completion:nil]; } } From de2142eaab6ed444a25e39a2e4c9830d21a13607 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Wed, 8 Nov 2023 10:39:08 +0000 Subject: [PATCH 32/63] Implemented skipCollapsed prop If not currently collapsed then removed collapsed from the valid detents. So when dragging down it will hide to match android --- NavigationReactNative/src/ios/NVBottomSheetManager.m | 1 + NavigationReactNative/src/ios/NVBottomSheetView.h | 1 + NavigationReactNative/src/ios/NVBottomSheetView.m | 3 +++ 3 files changed, 5 insertions(+) diff --git a/NavigationReactNative/src/ios/NVBottomSheetManager.m b/NavigationReactNative/src/ios/NVBottomSheetManager.m index 4e06b8a605..6f344df69c 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetManager.m +++ b/NavigationReactNative/src/ios/NVBottomSheetManager.m @@ -26,6 +26,7 @@ - (UIView *)view { RCT_EXPORT_VIEW_PROPERTY(halfExpandedRatio, double) RCT_EXPORT_VIEW_PROPERTY(hideable, BOOL) RCT_EXPORT_VIEW_PROPERTY(draggable, BOOL) +RCT_EXPORT_VIEW_PROPERTY(skipCollapsed, BOOL) RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger) RCT_EXPORT_VIEW_PROPERTY(onDetentChanged, RCTDirectEventBlock) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.h b/NavigationReactNative/src/ios/NVBottomSheetView.h index be5ca52b56..ff8486eca3 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetView.h @@ -13,6 +13,7 @@ API_AVAILABLE(ios(15.0)) @property (nonatomic, assign) double halfExpandedRatio; @property (nonatomic, assign) BOOL hideable; @property (nonatomic, assign) BOOL draggable; +@property (nonatomic, assign) BOOL skipCollapsed; @property (nonatomic, assign) NSInteger mostRecentEventCount; @property (nonatomic, copy) RCTDirectEventBlock onDetentChanged; diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 43b2b85527..8dd06309ad 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -98,6 +98,9 @@ - (void)didSetProps:(NSArray *)changedProps } [sheet animateChanges:^{ [sheet setDetents: [[self halfExpandedIdentifier] isEqual:UISheetPresentationControllerDetentIdentifierLarge] ? @[_collapsedDetent, _expandedDetent] : @[_collapsedDetent, _halfExpandedDetent, _expandedDetent]]; + if (_skipCollapsed && ![_detent isEqual:@"collapsed"]) { + [sheet setDetents: [[self halfExpandedIdentifier] isEqual:UISheetPresentationControllerDetentIdentifierLarge] ? @[ _expandedDetent] : @[_halfExpandedDetent, _expandedDetent]]; + } if (!_draggable) { [sheet setDetents: @[[newDetent isEqual:[self collapsedIdentifier]] ? _collapsedDetent : ([newDetent isEqual:[self expandedIdentifier]] ? _expandedDetent : _halfExpandedDetent)]]; } From e3aff74441de8c80eca89a3fdbaa2aa3202dca1f Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Wed, 8 Nov 2023 11:26:34 +0000 Subject: [PATCH 33/63] Ensured bottom sheet view garbage collected Navigated next and back and dealloc wasn't called when using self --- NavigationReactNative/src/ios/NVBottomSheetView.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 8dd06309ad..319654a16f 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -69,21 +69,21 @@ - (void)didSetProps:(NSArray *)changedProps _collapsedDetent = UISheetPresentationControllerDetent.mediumDetent; _expandedDetent = UISheetPresentationControllerDetent.largeDetent; _halfExpandedDetent = UISheetPresentationControllerDetent.largeDetent; + __weak typeof(self) weakSelf = self; if (@available(iOS 16.0, *)) { if (_peekHeight > 0) { _collapsedDetent = [UISheetPresentationControllerDetent customDetentWithIdentifier:@"collapsed" resolver:^CGFloat(id _Nonnull context) { - return self->_peekHeight; + return weakSelf.peekHeight; }]; } if (_expandedHeight > 0 || _expandedOffset > 0) { _expandedDetent = [UISheetPresentationControllerDetent customDetentWithIdentifier:@"expanded" resolver:^CGFloat(id _Nonnull context) { - return self->_expandedHeight > 0 ? self->_expandedHeight : context.maximumDetentValue - self->_expandedOffset; + return weakSelf.expandedHeight > 0 ? weakSelf.expandedHeight : context.maximumDetentValue - weakSelf.expandedOffset; }]; } - if (_halfExpandedRatio > 0) { _halfExpandedDetent = [UISheetPresentationControllerDetent customDetentWithIdentifier:@"halfExpanded" resolver:^CGFloat(id _Nonnull context) { - return self-> _halfExpandedRatio * context.maximumDetentValue; + return weakSelf.halfExpandedRatio * context.maximumDetentValue; }]; } } From 2f8983edd8ea71f931e453eb1e76a7bf88d8298b Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Wed, 8 Nov 2023 15:13:11 +0000 Subject: [PATCH 34/63] Ensured bottom sheet supports reload Tapping R (to reload) didn't dismiss the bottom sheet. Meant that it the old sheet was still around 'unattached' to react. Don't know why React Native doesn't remove from the view and invalidate?! It does invalidate the manager. So fired a notification from there and listened to it in the view and dismissed the sheet. Checked React Native's approach for Modal. The manager retains pointers to the views so it can dismiss. This sounds bad. Don't think Modals are ever deallocated?! Don't know why they didn't used notifications. --- NavigationReactNative/src/ios/NVBottomSheetManager.m | 8 +++++++- NavigationReactNative/src/ios/NVBottomSheetView.m | 12 ++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetManager.m b/NavigationReactNative/src/ios/NVBottomSheetManager.m index 6f344df69c..b9d1e82625 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetManager.m +++ b/NavigationReactNative/src/ios/NVBottomSheetManager.m @@ -2,8 +2,9 @@ #import #import +#import -@interface NVBottomSheetManager : RCTViewManager +@interface NVBottomSheetManager : RCTViewManager @end @@ -19,6 +20,11 @@ - (UIView *)view { } } +-(void)invalidate +{ + [[NSNotificationCenter defaultCenter] postNotificationName:@"dismissBottomSheet" object:nil]; +} + RCT_EXPORT_VIEW_PROPERTY(detent, NSString) RCT_EXPORT_VIEW_PROPERTY(peekHeight, NSInteger) RCT_EXPORT_VIEW_PROPERTY(expandedHeight, NSInteger) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 319654a16f..f72cbfe1cf 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -59,6 +59,7 @@ - (id)initWithBridge:(RCTBridge *)bridge _bottomSheetController.boundsDidChangeBlock = ^(CGRect newBounds) { [weakSelf notifyForBoundsChange:newBounds]; }; + [[NSNotificationCenter defaultCenter] addObserver:weakSelf selector:@selector(destroy:) name:@"dismissBottomSheet" object:nil]; } return self; } @@ -176,9 +177,20 @@ - (void)didUpdateReactSubviews { } +- (void) destroy:(NSNotification *) notification +{ + [self invalidate]; + if (_presented) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self->_bottomSheetController dismissViewControllerAnimated:NO completion:nil]; + }); + } +} + -(void)invalidate { [_displayLink invalidate]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)didMoveToWindow From 2cb269f2f8113dda8114d16ed3b92f18b062b903 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Wed, 8 Nov 2023 15:41:09 +0000 Subject: [PATCH 35/63] Implemented largestUndimmedDetent prop Keeps the background active when the sheet is opened at that detent or lower --- NavigationReactNative/src/BottomSheet.tsx | 3 ++- NavigationReactNative/src/ios/NVBottomSheetManager.m | 1 + NavigationReactNative/src/ios/NVBottomSheetView.h | 1 + NavigationReactNative/src/ios/NVBottomSheetView.m | 4 ++++ 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/NavigationReactNative/src/BottomSheet.tsx b/NavigationReactNative/src/BottomSheet.tsx index 7ade64d6ba..8a0214bdac 100644 --- a/NavigationReactNative/src/BottomSheet.tsx +++ b/NavigationReactNative/src/BottomSheet.tsx @@ -40,7 +40,7 @@ class BottomSheet extends React.Component { } render() { if (Platform.OS === 'ios' && +Platform.Version < 15) return null; - var { expandedHeight, expandedOffset, peekHeight, halfExpandedRatio, hideable, skipCollapsed, draggable, children } = this.props + var { expandedHeight, expandedOffset, peekHeight, halfExpandedRatio, largestUndimmedDetent, hideable, skipCollapsed, draggable, children } = this.props var { selectedDetent } = this.state; const detents = (UIManager as any).getViewManagerConfig('NVBottomSheet').Constants?.Detent; return ( @@ -52,6 +52,7 @@ class BottomSheet extends React.Component { expandedOffset={expandedOffset} fitToContents={expandedOffset == null} halfExpandedRatio={halfExpandedRatio} + largestUndimmedDetent={largestUndimmedDetent} hideable={hideable} skipCollapsed={skipCollapsed} draggable={draggable} diff --git a/NavigationReactNative/src/ios/NVBottomSheetManager.m b/NavigationReactNative/src/ios/NVBottomSheetManager.m index b9d1e82625..a457e50c39 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetManager.m +++ b/NavigationReactNative/src/ios/NVBottomSheetManager.m @@ -30,6 +30,7 @@ -(void)invalidate RCT_EXPORT_VIEW_PROPERTY(expandedHeight, NSInteger) RCT_EXPORT_VIEW_PROPERTY(expandedOffset, NSInteger) RCT_EXPORT_VIEW_PROPERTY(halfExpandedRatio, double) +RCT_EXPORT_VIEW_PROPERTY(largestUndimmedDetent, NSString) RCT_EXPORT_VIEW_PROPERTY(hideable, BOOL) RCT_EXPORT_VIEW_PROPERTY(draggable, BOOL) RCT_EXPORT_VIEW_PROPERTY(skipCollapsed, BOOL) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.h b/NavigationReactNative/src/ios/NVBottomSheetView.h index ff8486eca3..8a68b6c790 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetView.h @@ -11,6 +11,7 @@ API_AVAILABLE(ios(15.0)) @property (nonatomic, assign) NSInteger expandedHeight; @property (nonatomic, assign) NSInteger expandedOffset; @property (nonatomic, assign) double halfExpandedRatio; +@property (nonatomic, copy) NSString *largestUndimmedDetent; @property (nonatomic, assign) BOOL hideable; @property (nonatomic, assign) BOOL draggable; @property (nonatomic, assign) BOOL skipCollapsed; diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index f72cbfe1cf..5d13f4e454 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -98,6 +98,10 @@ - (void)didSetProps:(NSArray *)changedProps [[self reactViewController] presentViewController:_bottomSheetController animated:true completion:nil]; } [sheet animateChanges:^{ + [sheet setLargestUndimmedDetentIdentifier:nil]; + if ([_largestUndimmedDetent length]) { + [sheet setLargestUndimmedDetentIdentifier:[_largestUndimmedDetent isEqual: @"collapsed"] ? [self collapsedIdentifier] : ([_largestUndimmedDetent isEqual: @"expanded"] ? [self expandedIdentifier] : [self halfExpandedIdentifier])]; + } [sheet setDetents: [[self halfExpandedIdentifier] isEqual:UISheetPresentationControllerDetentIdentifierLarge] ? @[_collapsedDetent, _expandedDetent] : @[_collapsedDetent, _halfExpandedDetent, _expandedDetent]]; if (_skipCollapsed && ![_detent isEqual:@"collapsed"]) { [sheet setDetents: [[self halfExpandedIdentifier] isEqual:UISheetPresentationControllerDetentIdentifierLarge] ? @[ _expandedDetent] : @[_halfExpandedDetent, _expandedDetent]]; From d3149509b7d26dfe81b57e46adec72c963450489 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Wed, 8 Nov 2023 15:48:24 +0000 Subject: [PATCH 36/63] Renamed for clarity --- NavigationReactNative/src/ios/NVBottomSheetView.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 5d13f4e454..cc9257d5e1 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -53,7 +53,7 @@ - (id)initWithBridge:(RCTBridge *)bridge UIView *containerView = [UIView new]; containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; _bottomSheetController.view = containerView; - _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateView)]; + _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(resizeView)]; [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; __weak typeof(self) weakSelf = self; _bottomSheetController.boundsDidChangeBlock = ^(CGRect newBounds) { @@ -156,7 +156,7 @@ - (void)notifyForBoundsChange:(CGRect)newBounds } } -- (void)updateView { +- (void)resizeView { CGSize newSize = [[_bottomSheetController.view.layer.presentationLayer valueForKeyPath:@"frame.size"] CGSizeValue]; if (!CGSizeEqualToSize(_oldSize, newSize)) { _oldSize = newSize; From 6e4641810629d9545b9aa103bd7e51e1c9c022e0 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Wed, 8 Nov 2023 16:04:29 +0000 Subject: [PATCH 37/63] Excluded hidden and expanded from largest undimmed Neither of them make sense as largest undimmed. Has to be open have to be able to see the scene behind --- NavigationReactNative/src/ios/NVBottomSheetView.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index cc9257d5e1..ed7469cb13 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -100,7 +100,7 @@ - (void)didSetProps:(NSArray *)changedProps [sheet animateChanges:^{ [sheet setLargestUndimmedDetentIdentifier:nil]; if ([_largestUndimmedDetent length]) { - [sheet setLargestUndimmedDetentIdentifier:[_largestUndimmedDetent isEqual: @"collapsed"] ? [self collapsedIdentifier] : ([_largestUndimmedDetent isEqual: @"expanded"] ? [self expandedIdentifier] : [self halfExpandedIdentifier])]; + [sheet setLargestUndimmedDetentIdentifier:[_largestUndimmedDetent isEqual: @"collapsed"] ? [self collapsedIdentifier] : [self halfExpandedIdentifier]]; } [sheet setDetents: [[self halfExpandedIdentifier] isEqual:UISheetPresentationControllerDetentIdentifierLarge] ? @[_collapsedDetent, _expandedDetent] : @[_collapsedDetent, _halfExpandedDetent, _expandedDetent]]; if (_skipCollapsed && ![_detent isEqual:@"collapsed"]) { From 2e0c89c64eb6324aa342556c5ea7088da98e4d9c Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Wed, 8 Nov 2023 16:09:45 +0000 Subject: [PATCH 38/63] Typed the largestUndimmedDetent prop --- .../npm/navigation-react-native/navigation.react.native.d.ts | 4 ++++ types/navigation-react-native.d.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/build/npm/navigation-react-native/navigation.react.native.d.ts b/build/npm/navigation-react-native/navigation.react.native.d.ts index 02b8b563ce..2bb442421d 100644 --- a/build/npm/navigation-react-native/navigation.react.native.d.ts +++ b/build/npm/navigation-react-native/navigation.react.native.d.ts @@ -752,6 +752,10 @@ export interface BottomSheetProps { * Determines the height of the bottom sheet when it is half expanded */ halfExpandedRatio?: number; + /** + * Expanding above this resting state dims the view underneath the sheet + */ + largestUndimmedDetent?: 'collapsed' | 'halfExpanded'; /** * Indicates whether the bottom sheet can hide when it is swiped down */ diff --git a/types/navigation-react-native.d.ts b/types/navigation-react-native.d.ts index 02b8b563ce..2bb442421d 100644 --- a/types/navigation-react-native.d.ts +++ b/types/navigation-react-native.d.ts @@ -752,6 +752,10 @@ export interface BottomSheetProps { * Determines the height of the bottom sheet when it is half expanded */ halfExpandedRatio?: number; + /** + * Expanding above this resting state dims the view underneath the sheet + */ + largestUndimmedDetent?: 'collapsed' | 'halfExpanded'; /** * Indicates whether the bottom sheet can hide when it is swiped down */ From 8972fed2bbb416d75b9fcaa7be9c1840b477036b Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Wed, 8 Nov 2023 18:01:13 +0000 Subject: [PATCH 39/63] Matched existing code to reduce PR diff --- NavigationReactNative/src/BottomSheet.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/NavigationReactNative/src/BottomSheet.tsx b/NavigationReactNative/src/BottomSheet.tsx index 8a0214bdac..08b816c7f5 100644 --- a/NavigationReactNative/src/BottomSheet.tsx +++ b/NavigationReactNative/src/BottomSheet.tsx @@ -41,12 +41,11 @@ class BottomSheet extends React.Component { render() { if (Platform.OS === 'ios' && +Platform.Version < 15) return null; var { expandedHeight, expandedOffset, peekHeight, halfExpandedRatio, largestUndimmedDetent, hideable, skipCollapsed, draggable, children } = this.props - var { selectedDetent } = this.state; const detents = (UIManager as any).getViewManagerConfig('NVBottomSheet').Constants?.Detent; return ( Date: Wed, 8 Nov 2023 21:40:05 +0000 Subject: [PATCH 40/63] Removed prop else sheet stays across navigation If have largest undimmed the scene behind remains active. So can navigate and the sheet stays open?! but the sheet is controlled by a scene that's a crumb so it's suspended. What should happen here?! Removing for first version and can think about it more. Without undimming the sheet is modal so scene behind isn't active --- NavigationReactNative/src/BottomSheet.tsx | 3 +-- NavigationReactNative/src/ios/NVBottomSheetManager.m | 1 - NavigationReactNative/src/ios/NVBottomSheetView.h | 1 - NavigationReactNative/src/ios/NVBottomSheetView.m | 4 ---- .../npm/navigation-react-native/navigation.react.native.d.ts | 4 ---- types/navigation-react-native.d.ts | 4 ---- 6 files changed, 1 insertion(+), 16 deletions(-) diff --git a/NavigationReactNative/src/BottomSheet.tsx b/NavigationReactNative/src/BottomSheet.tsx index 08b816c7f5..04051f6ca0 100644 --- a/NavigationReactNative/src/BottomSheet.tsx +++ b/NavigationReactNative/src/BottomSheet.tsx @@ -40,7 +40,7 @@ class BottomSheet extends React.Component { } render() { if (Platform.OS === 'ios' && +Platform.Version < 15) return null; - var { expandedHeight, expandedOffset, peekHeight, halfExpandedRatio, largestUndimmedDetent, hideable, skipCollapsed, draggable, children } = this.props + var { expandedHeight, expandedOffset, peekHeight, halfExpandedRatio, hideable, skipCollapsed, draggable, children } = this.props const detents = (UIManager as any).getViewManagerConfig('NVBottomSheet').Constants?.Detent; return ( { expandedOffset={expandedOffset} fitToContents={expandedOffset == null} halfExpandedRatio={halfExpandedRatio} - largestUndimmedDetent={largestUndimmedDetent} hideable={hideable} skipCollapsed={skipCollapsed} draggable={draggable} diff --git a/NavigationReactNative/src/ios/NVBottomSheetManager.m b/NavigationReactNative/src/ios/NVBottomSheetManager.m index a457e50c39..b9d1e82625 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetManager.m +++ b/NavigationReactNative/src/ios/NVBottomSheetManager.m @@ -30,7 +30,6 @@ -(void)invalidate RCT_EXPORT_VIEW_PROPERTY(expandedHeight, NSInteger) RCT_EXPORT_VIEW_PROPERTY(expandedOffset, NSInteger) RCT_EXPORT_VIEW_PROPERTY(halfExpandedRatio, double) -RCT_EXPORT_VIEW_PROPERTY(largestUndimmedDetent, NSString) RCT_EXPORT_VIEW_PROPERTY(hideable, BOOL) RCT_EXPORT_VIEW_PROPERTY(draggable, BOOL) RCT_EXPORT_VIEW_PROPERTY(skipCollapsed, BOOL) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.h b/NavigationReactNative/src/ios/NVBottomSheetView.h index 8a68b6c790..ff8486eca3 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetView.h @@ -11,7 +11,6 @@ API_AVAILABLE(ios(15.0)) @property (nonatomic, assign) NSInteger expandedHeight; @property (nonatomic, assign) NSInteger expandedOffset; @property (nonatomic, assign) double halfExpandedRatio; -@property (nonatomic, copy) NSString *largestUndimmedDetent; @property (nonatomic, assign) BOOL hideable; @property (nonatomic, assign) BOOL draggable; @property (nonatomic, assign) BOOL skipCollapsed; diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index ed7469cb13..db3532e588 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -98,10 +98,6 @@ - (void)didSetProps:(NSArray *)changedProps [[self reactViewController] presentViewController:_bottomSheetController animated:true completion:nil]; } [sheet animateChanges:^{ - [sheet setLargestUndimmedDetentIdentifier:nil]; - if ([_largestUndimmedDetent length]) { - [sheet setLargestUndimmedDetentIdentifier:[_largestUndimmedDetent isEqual: @"collapsed"] ? [self collapsedIdentifier] : [self halfExpandedIdentifier]]; - } [sheet setDetents: [[self halfExpandedIdentifier] isEqual:UISheetPresentationControllerDetentIdentifierLarge] ? @[_collapsedDetent, _expandedDetent] : @[_collapsedDetent, _halfExpandedDetent, _expandedDetent]]; if (_skipCollapsed && ![_detent isEqual:@"collapsed"]) { [sheet setDetents: [[self halfExpandedIdentifier] isEqual:UISheetPresentationControllerDetentIdentifierLarge] ? @[ _expandedDetent] : @[_halfExpandedDetent, _expandedDetent]]; diff --git a/build/npm/navigation-react-native/navigation.react.native.d.ts b/build/npm/navigation-react-native/navigation.react.native.d.ts index 2bb442421d..02b8b563ce 100644 --- a/build/npm/navigation-react-native/navigation.react.native.d.ts +++ b/build/npm/navigation-react-native/navigation.react.native.d.ts @@ -752,10 +752,6 @@ export interface BottomSheetProps { * Determines the height of the bottom sheet when it is half expanded */ halfExpandedRatio?: number; - /** - * Expanding above this resting state dims the view underneath the sheet - */ - largestUndimmedDetent?: 'collapsed' | 'halfExpanded'; /** * Indicates whether the bottom sheet can hide when it is swiped down */ diff --git a/types/navigation-react-native.d.ts b/types/navigation-react-native.d.ts index 2bb442421d..02b8b563ce 100644 --- a/types/navigation-react-native.d.ts +++ b/types/navigation-react-native.d.ts @@ -752,10 +752,6 @@ export interface BottomSheetProps { * Determines the height of the bottom sheet when it is half expanded */ halfExpandedRatio?: number; - /** - * Expanding above this resting state dims the view underneath the sheet - */ - largestUndimmedDetent?: 'collapsed' | 'halfExpanded'; /** * Indicates whether the bottom sheet can hide when it is swiped down */ From 9ff5cb3f5c92c8eb2275a402a7731e96a7f36b6d Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 9 Nov 2023 10:58:39 +0000 Subject: [PATCH 41/63] Got app running on fabric Not done any bottom sheet stuff but this was needed to avoid runtime error --- NavigationReactNative/src/ios/NVBottomSheetComponentView.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm index be07b62513..3785ee6b2b 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm @@ -18,6 +18,8 @@ @implementation NVBottomSheetComponentView - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { + static const auto defaultProps = std::make_shared(); + _props = defaultProps; } return self; } From 7f995284a71aa570d82a2d8dd3a5087050798ca3 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 9 Nov 2023 11:43:05 +0000 Subject: [PATCH 42/63] Started copying bottom sheet from old arch to new Made detent a string so it can be passed to android and ios. Will convert back to int in java managers - consistent with autoCapitalize prop of SearchBar --- NavigationReactNative/src/BottomSheet.tsx | 2 +- .../src/BottomSheetNativeComponent.js | 3 +- .../src/ios/NVBottomSheetComponentView.h | 1 + .../src/ios/NVBottomSheetComponentView.mm | 74 ++++++++++++++++++- .../src/ios/NVBottomSheetView.m | 4 +- 5 files changed, 79 insertions(+), 5 deletions(-) diff --git a/NavigationReactNative/src/BottomSheet.tsx b/NavigationReactNative/src/BottomSheet.tsx index 04051f6ca0..479969e5c0 100644 --- a/NavigationReactNative/src/BottomSheet.tsx +++ b/NavigationReactNative/src/BottomSheet.tsx @@ -45,7 +45,7 @@ class BottomSheet extends React.Component { return ( , diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.h b/NavigationReactNative/src/ios/NVBottomSheetComponentView.h index 1671cd9e68..2a760c2421 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.h @@ -4,6 +4,7 @@ NS_ASSUME_NONNULL_BEGIN +API_AVAILABLE(ios(15.0)) @interface NVBottomSheetComponentView : RCTViewComponentView @end diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm index 3785ee6b2b..8aaee315f0 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm @@ -10,20 +10,88 @@ using namespace facebook::react; +@interface NVBottomSheetController : UIViewController + +@property (nonatomic, copy) void (^boundsDidChangeBlock)(CGRect newBounds); + +@end + +@implementation NVBottomSheetController +{ + CGRect _lastViewFrame; +} + +- (void)viewDidLayoutSubviews +{ + [super viewDidLayoutSubviews]; + if (self.boundsDidChangeBlock && !CGRectEqualToRect(_lastViewFrame, self.view.frame)) { + self.boundsDidChangeBlock(self.view.bounds); + _lastViewFrame = self.view.frame; + } +} + +@end + @interface NVBottomSheetComponentView () @end @implementation NVBottomSheetComponentView +{ + NVBottomSheetController *_bottomSheetController; + NVBottomSheetController *_oldBottomSheetController; + UIView *_reactSubview; + BOOL _presented; + NSInteger _nativeEventCount; + CADisplayLink *_displayLink; + UISheetPresentationControllerDetent *_collapsedDetent; + UISheetPresentationControllerDetent *_expandedDetent; + UISheetPresentationControllerDetent *_halfExpandedDetent; +} - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { static const auto defaultProps = std::make_shared(); _props = defaultProps; + _collapsedDetent = UISheetPresentationControllerDetent.mediumDetent; + _expandedDetent = UISheetPresentationControllerDetent.largeDetent; + _halfExpandedDetent = UISheetPresentationControllerDetent.largeDetent; } return self; } +- (void)ensureBottomSheetController +{ + if (!_bottomSheetController) { + [_oldBottomSheetController willMoveToParentViewController:nil]; + [_oldBottomSheetController.view removeFromSuperview]; + [_oldBottomSheetController removeFromParentViewController]; + UIView *containerView = [UIView new]; + containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + _bottomSheetController.view = containerView; + _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(resizeView)]; + [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + id __weak weakSelf = self; + _bottomSheetController.boundsDidChangeBlock = ^(CGRect newBounds) { + [weakSelf notifyForBoundsChange:newBounds]; + }; + } +} +- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps +{ + [self ensureBottomSheetController]; + const auto &newViewProps = *std::static_pointer_cast(props); + [super updateProps:props oldProps:oldProps]; +} + +- (void)notifyForBoundsChange:(CGRect)newBounds +{ +} + +- (void)resizeView +{ +} + #pragma mark - RCTComponentViewProtocol + (ComponentDescriptorProvider)componentDescriptorProvider @@ -35,7 +103,11 @@ + (ComponentDescriptorProvider)componentDescriptorProvider Class NVBottomSheetCls(void) { - return NVBottomSheetComponentView.class; + if (@available(iOS 15.0, *)) { + return NVBottomSheetComponentView.class; + } else { + return nil; + } } #endif diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index db3532e588..e09a041e4a 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -5,7 +5,6 @@ #import #import - @interface NVBottomSheetController : UIViewController @property (nonatomic, copy) void (^boundsDidChangeBlock)(CGRect newBounds); @@ -152,7 +151,8 @@ - (void)notifyForBoundsChange:(CGRect)newBounds } } -- (void)resizeView { +- (void)resizeView +{ CGSize newSize = [[_bottomSheetController.view.layer.presentationLayer valueForKeyPath:@"frame.size"] CGSizeValue]; if (!CGSizeEqualToSize(_oldSize, newSize)) { _oldSize = newSize; From d45f37d797b6a3758a03c3ad61599c205e76343c Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 9 Nov 2023 11:51:13 +0000 Subject: [PATCH 43/63] Added sheet controller so can share on old and new --- .../src/ios/NVBottomSheetController.h | 7 ++++++ .../src/ios/NVBottomSheetController.m | 25 +++++++++++++++++++ .../project.pbxproj | 4 +++ 3 files changed, 36 insertions(+) create mode 100644 NavigationReactNative/src/ios/NVBottomSheetController.h create mode 100644 NavigationReactNative/src/ios/NVBottomSheetController.m diff --git a/NavigationReactNative/src/ios/NVBottomSheetController.h b/NavigationReactNative/src/ios/NVBottomSheetController.h new file mode 100644 index 0000000000..4fca1da609 --- /dev/null +++ b/NavigationReactNative/src/ios/NVBottomSheetController.h @@ -0,0 +1,7 @@ +#import + +@interface NVBottomSheetController : UIViewController + +@property (nonatomic, copy) void (^boundsDidChangeBlock)(CGRect newBounds); + +@end diff --git a/NavigationReactNative/src/ios/NVBottomSheetController.m b/NavigationReactNative/src/ios/NVBottomSheetController.m new file mode 100644 index 0000000000..fa032778fe --- /dev/null +++ b/NavigationReactNative/src/ios/NVBottomSheetController.m @@ -0,0 +1,25 @@ +#import "NVBottomSheetController.h" + +@implementation NVBottomSheetController +{ + CGRect _lastViewFrame; +} + +- (id)init +{ + if (!(self = [super init])) { + return nil; + } + return self; +} + +- (void)viewDidLayoutSubviews +{ + [super viewDidLayoutSubviews]; + if (self.boundsDidChangeBlock && !CGRectEqualToRect(_lastViewFrame, self.view.frame)) { + self.boundsDidChangeBlock(self.view.bounds); + _lastViewFrame = self.view.frame; + } +} + +@end diff --git a/NavigationReactNative/src/ios/NavigationReactNative.xcodeproj/project.pbxproj b/NavigationReactNative/src/ios/NavigationReactNative.xcodeproj/project.pbxproj index cc607e1ed0..e76be5a4d9 100644 --- a/NavigationReactNative/src/ios/NavigationReactNative.xcodeproj/project.pbxproj +++ b/NavigationReactNative/src/ios/NavigationReactNative.xcodeproj/project.pbxproj @@ -29,6 +29,8 @@ 7025916528269ACE00A9DD13 /* NVTabBarPagerComponentView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NVTabBarPagerComponentView.mm; sourceTree = ""; }; 7025916728269ADF00A9DD13 /* NVTabBarPagerComponentView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NVTabBarPagerComponentView.h; sourceTree = ""; }; 70295FE72800768A0051A576 /* NVSceneComponentView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NVSceneComponentView.mm; sourceTree = ""; }; + 702E38C12AFCFEAE0009E0A7 /* NVBottomSheetController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NVBottomSheetController.m; sourceTree = ""; }; + 702E38C22AFCFEB70009E0A7 /* NVBottomSheetController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NVBottomSheetController.h; sourceTree = ""; }; 702FBDB62830F920002054B5 /* NVTitleBarComponentView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NVTitleBarComponentView.mm; sourceTree = ""; }; 702FBDB82830F92D002054B5 /* NVTitleBarComponentView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NVTitleBarComponentView.h; sourceTree = ""; }; 702FBDB928317EF9002054B5 /* NVStatusBarComponentView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NVStatusBarComponentView.mm; sourceTree = ""; }; @@ -142,6 +144,8 @@ 7020F6B520ECD07A00E7A74E = { isa = PBXGroup; children = ( + 702E38C22AFCFEB70009E0A7 /* NVBottomSheetController.h */, + 702E38C12AFCFEAE0009E0A7 /* NVBottomSheetController.m */, 707C4BF82AF64BF6008FC72E /* NVBottomSheetManager.m */, 707C4BF72AF64BE6008FC72E /* NVBottomSheetView.m */, 707C4BF62AF64BCD008FC72E /* NVBottomSheetView.h */, From c8b48cb896806fa3529308c38aac87b5debe8a33 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 9 Nov 2023 11:53:23 +0000 Subject: [PATCH 44/63] Imported sheet controller on old and new arch --- .../src/ios/NVBottomSheetComponentView.mm | 23 +------------------ .../src/ios/NVBottomSheetView.m | 23 +------------------ 2 files changed, 2 insertions(+), 44 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm index 8aaee315f0..22f261b192 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm @@ -1,5 +1,6 @@ #ifdef RCT_NEW_ARCH_ENABLED #import "NVBottomSheetComponentView.h" +#import "NVBottomSheetController.h" #import #import @@ -10,28 +11,6 @@ using namespace facebook::react; -@interface NVBottomSheetController : UIViewController - -@property (nonatomic, copy) void (^boundsDidChangeBlock)(CGRect newBounds); - -@end - -@implementation NVBottomSheetController -{ - CGRect _lastViewFrame; -} - -- (void)viewDidLayoutSubviews -{ - [super viewDidLayoutSubviews]; - if (self.boundsDidChangeBlock && !CGRectEqualToRect(_lastViewFrame, self.view.frame)) { - self.boundsDidChangeBlock(self.view.bounds); - _lastViewFrame = self.view.frame; - } -} - -@end - @interface NVBottomSheetComponentView () @end diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index e09a041e4a..e23e888738 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -1,32 +1,11 @@ #import "NVBottomSheetView.h" +#import "NVBottomSheetController.h" #import #import #import #import -@interface NVBottomSheetController : UIViewController - -@property (nonatomic, copy) void (^boundsDidChangeBlock)(CGRect newBounds); - -@end - -@implementation NVBottomSheetController -{ - CGRect _lastViewFrame; -} - -- (void)viewDidLayoutSubviews -{ - [super viewDidLayoutSubviews]; - if (self.boundsDidChangeBlock && !CGRectEqualToRect(_lastViewFrame, self.view.frame)) { - self.boundsDidChangeBlock(self.view.bounds); - _lastViewFrame = self.view.frame; - } -} - -@end - @implementation NVBottomSheetView { __weak RCTBridge *_bridge; From 4c4cf1816809808525986d27a27ef1f816830d94 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 9 Nov 2023 13:30:10 +0000 Subject: [PATCH 45/63] Copied over more bottom sheet code to new arch --- .../src/ios/NVBottomSheetComponentView.h | 2 +- .../src/ios/NVBottomSheetComponentView.mm | 79 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.h b/NavigationReactNative/src/ios/NVBottomSheetComponentView.h index 2a760c2421..ae182be72b 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.h +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.h @@ -5,7 +5,7 @@ NS_ASSUME_NONNULL_BEGIN API_AVAILABLE(ios(15.0)) -@interface NVBottomSheetComponentView : RCTViewComponentView +@interface NVBottomSheetComponentView : RCTViewComponentView @end diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm index 22f261b192..48f6f4d004 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm @@ -8,6 +8,7 @@ #import #import "RCTFabricComponentsPlugins.h" +#import using namespace facebook::react; @@ -60,9 +61,87 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & { [self ensureBottomSheetController]; const auto &newViewProps = *std::static_pointer_cast(props); + UISheetPresentationController *sheet = _bottomSheetController.sheetPresentationController; + _collapsedDetent = UISheetPresentationControllerDetent.mediumDetent; + _expandedDetent = UISheetPresentationControllerDetent.largeDetent; + _halfExpandedDetent = UISheetPresentationControllerDetent.largeDetent; + if (@available(iOS 16.0, *)) { + int peekHeight = newViewProps.peekHeight; + if (peekHeight > 0) { + _collapsedDetent = [UISheetPresentationControllerDetent customDetentWithIdentifier:@"collapsed" resolver:^CGFloat(id _Nonnull context) { + return peekHeight; + }]; + } + int expandedHeight = newViewProps.expandedHeight; + int expandedOffset = newViewProps.expandedOffset; + if (expandedHeight > 0 || expandedOffset > 0) { + _expandedDetent = [UISheetPresentationControllerDetent customDetentWithIdentifier:@"expanded" resolver:^CGFloat(id _Nonnull context) { + return expandedHeight > 0 ? expandedHeight : context.maximumDetentValue - expandedOffset; + }]; + } + float halfExpandedRatio = [@(newViewProps.halfExpandedRatio) floatValue]; + if (halfExpandedRatio > 0) { + _halfExpandedDetent = [UISheetPresentationControllerDetent customDetentWithIdentifier:@"halfExpanded" resolver:^CGFloat(id _Nonnull context) { + return halfExpandedRatio * context.maximumDetentValue; + }]; + } + } + NSInteger eventLag = _nativeEventCount - newViewProps.mostRecentEventCount; + NSString *detent = [[NSString alloc] initWithUTF8String: newViewProps.detent.c_str()]; + UISheetPresentationControllerDetentIdentifier newDetent = [detent isEqual: @"collapsed"] ? [self collapsedIdentifier] : ([detent isEqual: @"expanded"] ? [self expandedIdentifier] : [self halfExpandedIdentifier]); + if (![detent isEqual: @"hidden"]) { + if (self.window && !_presented) { + _presented = YES; + _bottomSheetController.sheetPresentationController.delegate = self; + _bottomSheetController.presentationController.delegate = self; + [[self reactViewController] presentViewController:_bottomSheetController animated:true completion:nil]; + } + [sheet animateChanges:^{ + [sheet setDetents: [[self halfExpandedIdentifier] isEqual:UISheetPresentationControllerDetentIdentifierLarge] ? @[_collapsedDetent, _expandedDetent] : @[_collapsedDetent, _halfExpandedDetent, _expandedDetent]]; + if (newViewProps.skipCollapsed && ![detent isEqual:@"collapsed"]) { + [sheet setDetents: [[self halfExpandedIdentifier] isEqual:UISheetPresentationControllerDetentIdentifierLarge] ? @[ _expandedDetent] : @[_halfExpandedDetent, _expandedDetent]]; + } + if (!newViewProps.draggable) { + [sheet setDetents: @[[newDetent isEqual:[self collapsedIdentifier]] ? _collapsedDetent : ([newDetent isEqual:[self expandedIdentifier]] ? _expandedDetent : _halfExpandedDetent)]]; + } + if (eventLag == 0 && [sheet selectedDetentIdentifier] != newDetent) { + sheet.selectedDetentIdentifier = newDetent; + } + }]; + } else { + [_bottomSheetController dismissViewControllerAnimated:YES completion:nil]; + _presented = NO; + } [super updateProps:props oldProps:oldProps]; } +- (UISheetPresentationControllerDetentIdentifier) collapsedIdentifier +{ + UISheetPresentationControllerDetentIdentifier collapsedIdentifier = UISheetPresentationControllerDetentIdentifierMedium; + if (@available(iOS 16.0, *)) { + collapsedIdentifier = _collapsedDetent.identifier; + } + return collapsedIdentifier; +} + +- (UISheetPresentationControllerDetentIdentifier) halfExpandedIdentifier +{ + UISheetPresentationControllerDetentIdentifier halfExpandedIdentifier = UISheetPresentationControllerDetentIdentifierLarge; + if (@available(iOS 16.0, *)) { + halfExpandedIdentifier = _halfExpandedDetent.identifier; + } + return halfExpandedIdentifier; +} + +- (UISheetPresentationControllerDetentIdentifier) expandedIdentifier +{ + UISheetPresentationControllerDetentIdentifier expandedIdentifier = UISheetPresentationControllerDetentIdentifierLarge; + if (@available(iOS 16.0, *)) { + expandedIdentifier = _expandedDetent.identifier; + } + return expandedIdentifier; +} + - (void)notifyForBoundsChange:(CGRect)newBounds { } From fbf9c701b79408ec7f671950d75fd47ceaa880ea Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 9 Nov 2023 13:54:28 +0000 Subject: [PATCH 46/63] Resized self because that's only option on fabric Can't resize a subview on new architecture, so changed old to check it worked --- NavigationReactNative/src/ios/NVBottomSheetView.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index e23e888738..81d5469aa6 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -125,7 +125,7 @@ - (void)notifyForBoundsChange:(CGRect)newBounds if (_reactSubview) { CAAnimation *dropping = [_bottomSheetController.view.layer animationForKey:@"bounds.size"]; if (!dropping) { - [_bridge.uiManager setSize:newBounds.size forView:_reactSubview]; + [_bridge.uiManager setSize:newBounds.size forView:self]; } } } @@ -135,7 +135,7 @@ - (void)resizeView CGSize newSize = [[_bottomSheetController.view.layer.presentationLayer valueForKeyPath:@"frame.size"] CGSizeValue]; if (!CGSizeEqualToSize(_oldSize, newSize)) { _oldSize = newSize; - [_bridge.uiManager setSize:newSize forView:_reactSubview]; + [_bridge.uiManager setSize:newSize forView:self]; } } From d931631be7c18ca478c025c877131720ce7385b2 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 9 Nov 2023 13:59:34 +0000 Subject: [PATCH 47/63] Removed redundant view Saw fabric modal didn't use a container view so though maybe old arch didn't need it either --- NavigationReactNative/src/ios/NVBottomSheetView.m | 3 --- 1 file changed, 3 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 81d5469aa6..261ad45846 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -28,9 +28,6 @@ - (id)initWithBridge:(RCTBridge *)bridge _collapsedDetent = UISheetPresentationControllerDetent.mediumDetent; _expandedDetent = UISheetPresentationControllerDetent.largeDetent; _halfExpandedDetent = UISheetPresentationControllerDetent.largeDetent; - UIView *containerView = [UIView new]; - containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; - _bottomSheetController.view = containerView; _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(resizeView)]; [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; __weak typeof(self) weakSelf = self; From 6d8388be3e1234822bce04f3173c0669a92eca14 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 9 Nov 2023 14:04:22 +0000 Subject: [PATCH 48/63] Removed redundant container Saw fabric modal didn't use one so figured could remove. Removed and tested in old arch --- NavigationReactNative/src/ios/NVBottomSheetComponentView.mm | 3 --- 1 file changed, 3 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm index 48f6f4d004..40c3c0e330 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm @@ -46,9 +46,6 @@ - (void)ensureBottomSheetController [_oldBottomSheetController willMoveToParentViewController:nil]; [_oldBottomSheetController.view removeFromSuperview]; [_oldBottomSheetController removeFromParentViewController]; - UIView *containerView = [UIView new]; - containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; - _bottomSheetController.view = containerView; _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(resizeView)]; [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; id __weak weakSelf = self; From 089a5df24dce27d4108b92780886bdd5faf3f243 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 9 Nov 2023 14:15:30 +0000 Subject: [PATCH 49/63] Presented the sheet It's not sizing correctly yet but it is showing as a sheet --- .../src/ios/NVBottomSheetComponentView.mm | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm index 40c3c0e330..dfcd1541d9 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm @@ -22,6 +22,7 @@ @implementation NVBottomSheetComponentView UIView *_reactSubview; BOOL _presented; NSInteger _nativeEventCount; + NSString *_detent; CADisplayLink *_displayLink; UISheetPresentationControllerDetent *_collapsedDetent; UISheetPresentationControllerDetent *_expandedDetent; @@ -46,6 +47,7 @@ - (void)ensureBottomSheetController [_oldBottomSheetController willMoveToParentViewController:nil]; [_oldBottomSheetController.view removeFromSuperview]; [_oldBottomSheetController removeFromParentViewController]; + _bottomSheetController = [[NVBottomSheetController alloc] init]; _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(resizeView)]; [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; id __weak weakSelf = self; @@ -84,9 +86,9 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & } } NSInteger eventLag = _nativeEventCount - newViewProps.mostRecentEventCount; - NSString *detent = [[NSString alloc] initWithUTF8String: newViewProps.detent.c_str()]; - UISheetPresentationControllerDetentIdentifier newDetent = [detent isEqual: @"collapsed"] ? [self collapsedIdentifier] : ([detent isEqual: @"expanded"] ? [self expandedIdentifier] : [self halfExpandedIdentifier]); - if (![detent isEqual: @"hidden"]) { + _detent = [[NSString alloc] initWithUTF8String: newViewProps.detent.c_str()]; + UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? [self collapsedIdentifier] : ([_detent isEqual: @"expanded"] ? [self expandedIdentifier] : [self halfExpandedIdentifier]); + if (![_detent isEqual: @"hidden"]) { if (self.window && !_presented) { _presented = YES; _bottomSheetController.sheetPresentationController.delegate = self; @@ -95,7 +97,7 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & } [sheet animateChanges:^{ [sheet setDetents: [[self halfExpandedIdentifier] isEqual:UISheetPresentationControllerDetentIdentifierLarge] ? @[_collapsedDetent, _expandedDetent] : @[_collapsedDetent, _halfExpandedDetent, _expandedDetent]]; - if (newViewProps.skipCollapsed && ![detent isEqual:@"collapsed"]) { + if (newViewProps.skipCollapsed && ![_detent isEqual:@"collapsed"]) { [sheet setDetents: [[self halfExpandedIdentifier] isEqual:UISheetPresentationControllerDetentIdentifierLarge] ? @[ _expandedDetent] : @[_halfExpandedDetent, _expandedDetent]]; } if (!newViewProps.draggable) { @@ -147,8 +149,30 @@ - (void)resizeView { } +- (void)didMoveToWindow +{ + [super didMoveToWindow]; + if (![_detent isEqual: @"hidden"] && !_presented) { + _presented = YES; + _bottomSheetController.sheetPresentationController.delegate = self; + _bottomSheetController.presentationController.delegate = self; + [[self reactViewController] presentViewController:_bottomSheetController animated:true completion:nil]; + } +} + #pragma mark - RCTComponentViewProtocol +- (void)mountChildComponentView:(UIView *)childComponentView index:(NSInteger)index +{ + [self ensureBottomSheetController]; + [_bottomSheetController.view insertSubview:childComponentView atIndex:index]; +} + +- (void)unmountChildComponentView:(UIView *)childComponentView index:(NSInteger)index +{ + [childComponentView removeFromSuperview]; +} + + (ComponentDescriptorProvider)componentDescriptorProvider { return concreteComponentDescriptorProvider(); From 88880f250387f9740020fdf4d2b1542a5d6523bb Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 9 Nov 2023 16:12:27 +0000 Subject: [PATCH 50/63] Resized view when bounds change on fabric Created the cpp state files etc. Copied from TitleBar/SearchBar --- .../src/BottomSheetNativeComponent.js | 1 + .../src/cpp/navigationreactnative.h | 1 + .../NVBottomSheetComponentDescriptor.h | 41 +++++++++++++++++ .../NVBottomSheetShadowNode.cpp | 9 ++++ .../NVBottomSheetShadowNode.h | 29 ++++++++++++ .../NVBottomSheetState.cpp | 14 ++++++ .../NVBottomSheetState.h | 45 +++++++++++++++++++ .../src/ios/NVBottomSheetComponentView.mm | 13 ++++++ .../src/ios/NVSearchBarComponentView.mm | 2 +- 9 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetComponentDescriptor.h create mode 100644 NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetShadowNode.cpp create mode 100644 NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetShadowNode.h create mode 100644 NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetState.cpp create mode 100644 NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetState.h diff --git a/NavigationReactNative/src/BottomSheetNativeComponent.js b/NavigationReactNative/src/BottomSheetNativeComponent.js index 2e2f5f0cf2..6ebbb250c2 100644 --- a/NavigationReactNative/src/BottomSheetNativeComponent.js +++ b/NavigationReactNative/src/BottomSheetNativeComponent.js @@ -25,4 +25,5 @@ type NativeProps = $ReadOnly<{| export default (codegenNativeComponent( 'NVBottomSheet', + {interfaceOnly: true} ): HostComponent); diff --git a/NavigationReactNative/src/cpp/navigationreactnative.h b/NavigationReactNative/src/cpp/navigationreactnative.h index 87547b45da..ae1bfee2a1 100644 --- a/NavigationReactNative/src/cpp/navigationreactnative.h +++ b/NavigationReactNative/src/cpp/navigationreactnative.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetComponentDescriptor.h b/NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetComponentDescriptor.h new file mode 100644 index 0000000000..83b2701b9d --- /dev/null +++ b/NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetComponentDescriptor.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include "NVBottomSheetShadowNode.h" +#include + +namespace facebook { +namespace react { + +class NVBottomSheetComponentDescriptor final + : public ConcreteComponentDescriptor { + public: + using ConcreteComponentDescriptor::ConcreteComponentDescriptor; + + void adopt(ShadowNode::Unshared const &shadowNode) const override { + react_native_assert( + std::dynamic_pointer_cast(shadowNode)); + auto screenShadowNode = + std::static_pointer_cast(shadowNode); + + react_native_assert( + std::dynamic_pointer_cast(screenShadowNode)); + auto layoutableShadowNode = + std::static_pointer_cast(screenShadowNode); + + auto state = + std::static_pointer_cast( + shadowNode->getState()); + auto stateData = state->getData(); + + if (stateData.frameSize.width != 0 && stateData.frameSize.height != 0) { + layoutableShadowNode->setSize( + Size{stateData.frameSize.width, stateData.frameSize.height}); + } + + ConcreteComponentDescriptor::adopt(shadowNode); + } +}; + +} // namespace react +} // namespace facebook \ No newline at end of file diff --git a/NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetShadowNode.cpp b/NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetShadowNode.cpp new file mode 100644 index 0000000000..450f5d6099 --- /dev/null +++ b/NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetShadowNode.cpp @@ -0,0 +1,9 @@ +#include "NVBottomSheetShadowNode.h" + +namespace facebook { +namespace react { + +extern const char NVBottomSheetComponentName[] = "NVBottomSheet"; + +} // namespace react +} // namespace facebook \ No newline at end of file diff --git a/NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetShadowNode.h b/NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetShadowNode.h new file mode 100644 index 0000000000..bfee47082d --- /dev/null +++ b/NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetShadowNode.h @@ -0,0 +1,29 @@ +#pragma once + +#include "NVBottomSheetState.h" +#include +#include +#include + +namespace facebook { +namespace react { + +JSI_EXPORT extern const char NVBottomSheetComponentName[]; + +class JSI_EXPORT NVBottomSheetShadowNode final : public ConcreteViewShadowNode< + NVBottomSheetComponentName, + NVBottomSheetProps, + NVBottomSheetEventEmitter, + NVBottomSheetState> { + public: + using ConcreteViewShadowNode::ConcreteViewShadowNode; + + static ShadowNodeTraits BaseTraits() { + auto traits = ConcreteViewShadowNode::BaseTraits(); + traits.set(ShadowNodeTraits::Trait::RootNodeKind); + return traits; + } +}; + +} // namespace react +} // namespace facebook \ No newline at end of file diff --git a/NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetState.cpp b/NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetState.cpp new file mode 100644 index 0000000000..5c2d324881 --- /dev/null +++ b/NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetState.cpp @@ -0,0 +1,14 @@ +#include "NVBottomSheetState.h" + +namespace facebook { +namespace react { + +#ifdef ANDROID +folly::dynamic NVBottomSheetState::getDynamic() const { + return folly::dynamic::object("frameWidth", frameSize.width)( + "frameHeight", frameSize.height); +} +#endif + +} // namespace react +} // namespace facebook \ No newline at end of file diff --git a/NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetState.h b/NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetState.h new file mode 100644 index 0000000000..80fe65466b --- /dev/null +++ b/NavigationReactNative/src/cpp/react/renderer/components/navigationreactnative/NVBottomSheetState.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#ifdef ANDROID +#include +#include +#include +#endif + +namespace facebook { +namespace react { + +class JSI_EXPORT NVBottomSheetState final { + public: + using Shared = std::shared_ptr; + + NVBottomSheetState(){}; + NVBottomSheetState(Size frameSize_) : frameSize(frameSize_){}; + +#ifdef ANDROID + NVBottomSheetState( + NVBottomSheetState const &previousState, + folly::dynamic data) + : frameSize(Size{ + (Float)data["frameWidth"].getDouble(), + (Float)data["frameHeight"].getDouble()}){}; +#endif + + const Size frameSize{}; + +#ifdef ANDROID + folly::dynamic getDynamic() const; + MapBuffer getMapBuffer() const { + return MapBufferBuilder::EMPTY(); + }; + +#endif + +#pragma mark - Getters +}; + +} // namespace react +} // namespace facebook \ No newline at end of file diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm index dfcd1541d9..34fa1efa02 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm @@ -6,8 +6,10 @@ #import #import #import +#import #import "RCTFabricComponentsPlugins.h" +#import #import using namespace facebook::react; @@ -27,6 +29,7 @@ @implementation NVBottomSheetComponentView UISheetPresentationControllerDetent *_collapsedDetent; UISheetPresentationControllerDetent *_expandedDetent; UISheetPresentationControllerDetent *_halfExpandedDetent; + NVBottomSheetShadowNode::ConcreteState::Shared _state; } - (instancetype)initWithFrame:(CGRect)frame @@ -143,6 +146,10 @@ - (UISheetPresentationControllerDetentIdentifier) expandedIdentifier - (void)notifyForBoundsChange:(CGRect)newBounds { + if (_state != nullptr) { + auto newState = NVBottomSheetState{RCTSizeFromCGSize(newBounds.size)}; + _state->updateState(std::move(newState)); + } } - (void)resizeView @@ -173,6 +180,12 @@ - (void)unmountChildComponentView:(UIView *)childCompo [childComponentView removeFromSuperview]; } +- (void)updateState:(facebook::react::State::Shared const &)state + oldState:(facebook::react::State::Shared const &)oldState +{ + _state = std::static_pointer_cast(state); +} + + (ComponentDescriptorProvider)componentDescriptorProvider { return concreteComponentDescriptorProvider(); diff --git a/NavigationReactNative/src/ios/NVSearchBarComponentView.mm b/NavigationReactNative/src/ios/NVSearchBarComponentView.mm index f5800716e7..399dfdec01 100644 --- a/NavigationReactNative/src/ios/NVSearchBarComponentView.mm +++ b/NavigationReactNative/src/ios/NVSearchBarComponentView.mm @@ -222,7 +222,7 @@ - (void)prepareForRecycle - (void)notifyForBoundsChange:(CGRect)newBounds { if (_state != nullptr) { - auto newState = NVSearchBarState{RCTSizeFromCGSize(self.bounds.size)}; + auto newState = NVSearchBarState{RCTSizeFromCGSize(newBounds.size)}; _state->updateState(std::move(newState)); } } From a30e5856e221e68a1ec12b679c96ff90b4307957 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 9 Nov 2023 16:14:07 +0000 Subject: [PATCH 51/63] Resized view on dropping sheet --- .../src/ios/NVBottomSheetComponentView.mm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm index 34fa1efa02..cffdc1466c 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm @@ -21,7 +21,7 @@ @implementation NVBottomSheetComponentView { NVBottomSheetController *_bottomSheetController; NVBottomSheetController *_oldBottomSheetController; - UIView *_reactSubview; + CGSize _oldSize; BOOL _presented; NSInteger _nativeEventCount; NSString *_detent; @@ -154,6 +154,12 @@ - (void)notifyForBoundsChange:(CGRect)newBounds - (void)resizeView { + CGSize newSize = [[_bottomSheetController.view.layer.presentationLayer valueForKeyPath:@"frame.size"] CGSizeValue]; + if (!CGSizeEqualToSize(_oldSize, newSize)) { + _oldSize = newSize; + auto newState = NVBottomSheetState{RCTSizeFromCGSize(newSize)}; + _state->updateState(std::move(newState)); + } } - (void)didMoveToWindow From 6243c10252f2e754cba47441d736e88355202bb8 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 9 Nov 2023 16:26:03 +0000 Subject: [PATCH 52/63] Recycled view so can be reused Seems a bit iffy on tapping r (reload) - sheet doesn't always come back?! --- .../src/ios/NVBottomSheetComponentView.mm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm index cffdc1466c..9f0a4b012b 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm @@ -144,6 +144,17 @@ - (UISheetPresentationControllerDetentIdentifier) expandedIdentifier return expandedIdentifier; } +- (void)prepareForRecycle +{ + [super prepareForRecycle]; + _state.reset(); + _nativeEventCount = 0; + _oldBottomSheetController = _bottomSheetController; + _bottomSheetController = nil; + _oldSize = CGSizeZero; + _presented = NO; +} + - (void)notifyForBoundsChange:(CGRect)newBounds { if (_state != nullptr) { From 78b543fd0985013cbc3320a8c297202aa0de5e96 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 9 Nov 2023 16:28:08 +0000 Subject: [PATCH 53/63] Stopped running display link when recycled --- NavigationReactNative/src/ios/NVBottomSheetComponentView.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm index 9f0a4b012b..3d849ccf70 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm @@ -153,6 +153,7 @@ - (void)prepareForRecycle _bottomSheetController = nil; _oldSize = CGSizeZero; _presented = NO; + [_displayLink invalidate]; } - (void)notifyForBoundsChange:(CGRect)newBounds From d627aeec8154c0a793821ef875fd89cff740e4e3 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 9 Nov 2023 16:36:46 +0000 Subject: [PATCH 54/63] Checked for consistency with bounds listener --- NavigationReactNative/src/ios/NVBottomSheetComponentView.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm index 3d849ccf70..d92da79f9b 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm @@ -167,7 +167,7 @@ - (void)notifyForBoundsChange:(CGRect)newBounds - (void)resizeView { CGSize newSize = [[_bottomSheetController.view.layer.presentationLayer valueForKeyPath:@"frame.size"] CGSizeValue]; - if (!CGSizeEqualToSize(_oldSize, newSize)) { + if (!CGSizeEqualToSize(_oldSize, newSize) && _state != nullptr) { _oldSize = newSize; auto newState = NVBottomSheetState{RCTSizeFromCGSize(newSize)}; _state->updateState(std::move(newState)); From 74f3e0077182e6e9e799b21456c8567e64f2ad65 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Fri, 10 Nov 2023 11:02:48 +0000 Subject: [PATCH 55/63] Raised onDetentChanged event on new arch --- .../src/BottomSheetNativeComponent.js | 2 +- .../src/ios/NVBottomSheetComponentView.mm | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/NavigationReactNative/src/BottomSheetNativeComponent.js b/NavigationReactNative/src/BottomSheetNativeComponent.js index 6ebbb250c2..5c445f0cb0 100644 --- a/NavigationReactNative/src/BottomSheetNativeComponent.js +++ b/NavigationReactNative/src/BottomSheetNativeComponent.js @@ -18,7 +18,7 @@ type NativeProps = $ReadOnly<{| draggable: boolean, sheetHeight: Double, onDetentChanged: DirectEventHandler<$ReadOnly<{| - detent: Int32, + detent: string, eventCount: Int32, |}>>, |}>; diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm index d92da79f9b..a110c48a50 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm @@ -25,6 +25,7 @@ @implementation NVBottomSheetComponentView BOOL _presented; NSInteger _nativeEventCount; NSString *_detent; + BOOL _hideable; CADisplayLink *_displayLink; UISheetPresentationControllerDetent *_collapsedDetent; UISheetPresentationControllerDetent *_expandedDetent; @@ -88,6 +89,7 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & }]; } } + _hideable = newViewProps.hideable; NSInteger eventLag = _nativeEventCount - newViewProps.mostRecentEventCount; _detent = [[NSString alloc] initWithUTF8String: newViewProps.detent.c_str()]; UISheetPresentationControllerDetentIdentifier newDetent = [_detent isEqual: @"collapsed"] ? [self collapsedIdentifier] : ([_detent isEqual: @"expanded"] ? [self expandedIdentifier] : [self halfExpandedIdentifier]); @@ -185,6 +187,37 @@ - (void)didMoveToWindow } } +- (void)sheetPresentationControllerDidChangeSelectedDetentIdentifier:(UISheetPresentationController *)sheetPresentationController +{ + _nativeEventCount++; + if (_eventEmitter != nullptr) { + UISheetPresentationControllerDetentIdentifier detentId = sheetPresentationController.selectedDetentIdentifier; + std::static_pointer_cast(_eventEmitter) + ->onDetentChanged(NVBottomSheetEventEmitter::OnDetentChanged{ + .detent = std::string([[[self collapsedIdentifier] isEqual:detentId] ? @"collapsed" : ([[self expandedIdentifier] isEqual:detentId] ? @"expanded" : @"halfExpanded") UTF8String]), + .eventCount = static_cast(_nativeEventCount), + }); + } +} + +- (BOOL)presentationControllerShouldDismiss:(UIPresentationController *)presentationController +{ + return _hideable; +} + +- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController +{ + _nativeEventCount++; + if (_eventEmitter != nullptr) { + std::static_pointer_cast(_eventEmitter) + ->onDetentChanged(NVBottomSheetEventEmitter::OnDetentChanged{ + .detent = std::string([@"hidden" UTF8String]), + .eventCount = static_cast(_nativeEventCount), + }); + } +} + + #pragma mark - RCTComponentViewProtocol - (void)mountChildComponentView:(UIView *)childComponentView index:(NSInteger)index From e68ee291e581a53b842878e293334560c3287d48 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Fri, 10 Nov 2023 11:13:00 +0000 Subject: [PATCH 56/63] Removed blank line --- NavigationReactNative/src/ios/NVBottomSheetComponentView.mm | 1 - 1 file changed, 1 deletion(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm index a110c48a50..d65471f715 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm @@ -217,7 +217,6 @@ - (void)presentationControllerDidDismiss:(UIPresentationController *)presentatio } } - #pragma mark - RCTComponentViewProtocol - (void)mountChildComponentView:(UIView *)childComponentView index:(NSInteger)index From edae9cbad4f35cc7d72276188575df6228c60474 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Fri, 10 Nov 2023 11:56:52 +0000 Subject: [PATCH 57/63] Ensured bottom sheet works with reload (tap r) The sheet didn't reopen after tapping r. Dismissing the controller when recycling fixes this (worked out by comparing with fabric modal - although it does it in didMoveToSuperview - prefer recycle) --- NavigationReactNative/src/ios/NVBottomSheetComponentView.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm index d65471f715..87414b124c 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm @@ -151,6 +151,7 @@ - (void)prepareForRecycle [super prepareForRecycle]; _state.reset(); _nativeEventCount = 0; + [_bottomSheetController dismissViewControllerAnimated:YES completion:nil]; _oldBottomSheetController = _bottomSheetController; _bottomSheetController = nil; _oldSize = CGSizeZero; From f38840a0dd3ad9072c7cff90c407593ebb4c5a10 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Fri, 10 Nov 2023 11:58:35 +0000 Subject: [PATCH 58/63] Removed pointless dismiss animation when recycling --- NavigationReactNative/src/ios/NVBottomSheetComponentView.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm index 87414b124c..6faa37c69d 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm @@ -151,7 +151,7 @@ - (void)prepareForRecycle [super prepareForRecycle]; _state.reset(); _nativeEventCount = 0; - [_bottomSheetController dismissViewControllerAnimated:YES completion:nil]; + [_bottomSheetController dismissViewControllerAnimated:NO completion:nil]; _oldBottomSheetController = _bottomSheetController; _bottomSheetController = nil; _oldSize = CGSizeZero; From 527ab1823a3c9e661a22080d2df8f52b70ce081f Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Fri, 10 Nov 2023 12:18:12 +0000 Subject: [PATCH 59/63] Ensured can touch views in the bottom sheet Copied the code from React Native modal --- NavigationReactNative/src/ios/NVBottomSheetComponentView.mm | 5 +++++ NavigationReactNative/src/ios/NVBottomSheetView.m | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm index 6faa37c69d..9797538009 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm +++ b/NavigationReactNative/src/ios/NVBottomSheetComponentView.mm @@ -10,6 +10,7 @@ #import "RCTFabricComponentsPlugins.h" #import +#import #import using namespace facebook::react; @@ -27,6 +28,7 @@ @implementation NVBottomSheetComponentView NSString *_detent; BOOL _hideable; CADisplayLink *_displayLink; + RCTSurfaceTouchHandler *_touchHandler; UISheetPresentationControllerDetent *_collapsedDetent; UISheetPresentationControllerDetent *_expandedDetent; UISheetPresentationControllerDetent *_halfExpandedDetent; @@ -38,6 +40,7 @@ - (instancetype)initWithFrame:(CGRect)frame if (self = [super initWithFrame:frame]) { static const auto defaultProps = std::make_shared(); _props = defaultProps; + _touchHandler = [RCTSurfaceTouchHandler new]; _collapsedDetent = UISheetPresentationControllerDetent.mediumDetent; _expandedDetent = UISheetPresentationControllerDetent.largeDetent; _halfExpandedDetent = UISheetPresentationControllerDetent.largeDetent; @@ -223,12 +226,14 @@ - (void)presentationControllerDidDismiss:(UIPresentationController *)presentatio - (void)mountChildComponentView:(UIView *)childComponentView index:(NSInteger)index { [self ensureBottomSheetController]; + [_touchHandler attachToView:childComponentView]; [_bottomSheetController.view insertSubview:childComponentView atIndex:index]; } - (void)unmountChildComponentView:(UIView *)childComponentView index:(NSInteger)index { [childComponentView removeFromSuperview]; + [_touchHandler detachFromView:childComponentView]; } - (void)updateState:(facebook::react::State::Shared const &)state diff --git a/NavigationReactNative/src/ios/NVBottomSheetView.m b/NavigationReactNative/src/ios/NVBottomSheetView.m index 261ad45846..0892743e8d 100644 --- a/NavigationReactNative/src/ios/NVBottomSheetView.m +++ b/NavigationReactNative/src/ios/NVBottomSheetView.m @@ -3,6 +3,7 @@ #import #import +#import #import #import @@ -15,6 +16,7 @@ @implementation NVBottomSheetView BOOL _presented; NSInteger _nativeEventCount; CADisplayLink *_displayLink; + RCTTouchHandler *_touchHandler; UISheetPresentationControllerDetent *_collapsedDetent; UISheetPresentationControllerDetent *_expandedDetent; UISheetPresentationControllerDetent *_halfExpandedDetent; @@ -25,6 +27,7 @@ - (id)initWithBridge:(RCTBridge *)bridge if (self = [super init]) { _bridge = bridge; _bottomSheetController = [[NVBottomSheetController alloc] init]; + _touchHandler = [[RCTTouchHandler alloc] initWithBridge:bridge]; _collapsedDetent = UISheetPresentationControllerDetent.mediumDetent; _expandedDetent = UISheetPresentationControllerDetent.largeDetent; _halfExpandedDetent = UISheetPresentationControllerDetent.largeDetent; @@ -139,6 +142,7 @@ - (void)resizeView - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex { [super insertReactSubview:subview atIndex:atIndex]; + [_touchHandler attachToView:subview]; [_bottomSheetController.view insertSubview:subview atIndex:0]; _reactSubview = subview; } @@ -146,6 +150,7 @@ - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex - (void)removeReactSubview:(UIView *)subview { [super removeReactSubview:subview]; + [_touchHandler detachFromView:subview]; _reactSubview = nil; } From af95f04a71604892a83eb5a07edcb88e7ff620ba Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Fri, 10 Nov 2023 13:02:36 +0000 Subject: [PATCH 60/63] Converted detent to int Made it a string so it's same on android and ios (ios doesn't use constants) --- .../java/com/navigation/reactnative/BottomSheetManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomSheetManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomSheetManager.java index 30a1c26f77..772e5bc4a8 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomSheetManager.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomSheetManager.java @@ -28,8 +28,8 @@ protected BottomSheetView createViewInstance(@NonNull ThemedReactContext reactCo } @ReactProp(name = "detent") - public void setDetent(BottomSheetView view, int detent) { - view.pendingDetent = detent; + public void setDetent(BottomSheetView view, String detent) { + view.pendingDetent = Integer.parseInt(detent); } @ReactProp(name = "mostRecentEventCount") From 595a1ee8eb682734910c2dd31f415dc44968265c Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Fri, 10 Nov 2023 13:10:35 +0000 Subject: [PATCH 61/63] Converted detent to int Made detent string because it's string on ios. Also added expandedHeight prop because forced to on fabric --- .../navigation/reactnative/BottomSheetViewManager.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomSheetViewManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomSheetViewManager.java index 3bea03f739..5df9c076de 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomSheetViewManager.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomSheetViewManager.java @@ -43,8 +43,8 @@ protected BottomSheetView createViewInstance(@NonNull ThemedReactContext reactCo } @ReactProp(name = "detent") - public void setDetent(BottomSheetView view, int detent) { - view.pendingDetent = detent; + public void setDetent(BottomSheetView view, String detent) { + view.pendingDetent = Integer.parseInt(detent); } @ReactProp(name = "mostRecentEventCount") @@ -57,6 +57,10 @@ public void setPeekHeight(BottomSheetView view, int peekHeight) { view.bottomSheetBehavior.setPeekHeight((int) PixelUtil.toPixelFromDIP(peekHeight), true); } + @Override + public void setExpandedHeight(BottomSheetView view, int value) { + } + @ReactProp(name = "expandedOffset") public void setExpandedOffset(BottomSheetView view, int expandedOffset) { view.bottomSheetBehavior.setExpandedOffset((int) PixelUtil.toPixelFromDIP(expandedOffset)); From c96c899c96e454b2a03681093cb13248a6e4644f Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Fri, 10 Nov 2023 14:22:31 +0000 Subject: [PATCH 62/63] Ensured scene behind is touchable when hidden Tried on ios 15 in the medley sample and the north button was pressable when sheet was hidden. iOS doesn't resize when it hides, so the height doesn't go below the collapsed height. On ios 15 this collapsed height blocked the north button --- NavigationReactNative/src/BottomSheet.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/NavigationReactNative/src/BottomSheet.tsx b/NavigationReactNative/src/BottomSheet.tsx index 479969e5c0..1178c16d8c 100644 --- a/NavigationReactNative/src/BottomSheet.tsx +++ b/NavigationReactNative/src/BottomSheet.tsx @@ -64,6 +64,7 @@ class BottomSheet extends React.Component { expandedOffset != null ? { top: expandedOffset } : null, expandedHeight == null && expandedOffset == null ? { top: 0 } : null, Platform.OS === 'ios' ? { height: undefined, top: undefined } : null, + this.state.selectedDetent === 'hidden' ? { height: 0 } : null, ]} > {children} From c9ac65c30eb13d141cfaa436b50eacf5b98f04fd Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Fri, 10 Nov 2023 16:52:46 +0000 Subject: [PATCH 63/63] Hid using display for ios fabric The north button still wasn't pressable using height on fabric on ios. Changing to display works on old and new arch --- NavigationReactNative/src/BottomSheet.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NavigationReactNative/src/BottomSheet.tsx b/NavigationReactNative/src/BottomSheet.tsx index 1178c16d8c..fbd7e88ef8 100644 --- a/NavigationReactNative/src/BottomSheet.tsx +++ b/NavigationReactNative/src/BottomSheet.tsx @@ -64,7 +64,7 @@ class BottomSheet extends React.Component { expandedOffset != null ? { top: expandedOffset } : null, expandedHeight == null && expandedOffset == null ? { top: 0 } : null, Platform.OS === 'ios' ? { height: undefined, top: undefined } : null, - this.state.selectedDetent === 'hidden' ? { height: 0 } : null, + { display: this.state.selectedDetent !== 'hidden' ? 'flex' : 'none' }, ]} > {children}