@lodev09/react-native-true-sheet 3.11.13 → 3.11.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -382,7 +382,6 @@ using namespace facebook::react;
382
382
  [_controller.view addSubview:_containerView];
383
383
  [LayoutUtil pinView:_containerView toParentView:_controller.view edges:UIRectEdgeAll];
384
384
  [_controller.view bringSubviewToFront:_containerView];
385
- _containerView.accessibilityViewIsModal = YES;
386
385
  _controller.accessibilityContentView = _containerView;
387
386
  [_controller setupAccessibilityContainer];
388
387
 
@@ -43,6 +43,7 @@ static char TrueSheetAccessibilityWindowPreviousElementsKey;
43
43
  - (void)restoreWindowAccessibilityElements;
44
44
  - (void)setSheetAccessibilityElementsHidden:(BOOL)hidden;
45
45
  - (void)setAccessibilityContentElement:(UIView *)contentView;
46
+ - (NSArray *)accessibilityGrabberElements;
46
47
  - (void)endInteractiveDismissState;
47
48
  - (void)emitInteractivePosition;
48
49
  - (void)animateInteractiveContainerToTransform:(CGAffineTransform)transform
@@ -52,6 +53,20 @@ static char TrueSheetAccessibilityWindowPreviousElementsKey;
52
53
 
53
54
  @end
54
55
 
56
+ // VoiceOver delivers the two-finger Z (escape) gesture by walking up the view
57
+ // hierarchy from the focused element, so the sheet's root view handles it.
58
+ @interface TrueSheetControllerView : UIView
59
+ @property (nonatomic, weak) TrueSheetViewController *controller;
60
+ @end
61
+
62
+ @implementation TrueSheetControllerView
63
+
64
+ - (BOOL)accessibilityPerformEscape {
65
+ return [self.controller accessibilityPerformEscape];
66
+ }
67
+
68
+ @end
69
+
55
70
  @implementation TrueSheetViewController {
56
71
  TrueSheetPositionState _lastEmittedPositionState;
57
72
  CGFloat _lastWidth;
@@ -214,6 +229,12 @@ static char TrueSheetAccessibilityWindowPreviousElementsKey;
214
229
 
215
230
  #pragma mark - View Lifecycle
216
231
 
232
+ - (void)loadView {
233
+ TrueSheetControllerView *view = [[TrueSheetControllerView alloc] init];
234
+ view.controller = self;
235
+ self.view = view;
236
+ }
237
+
217
238
  - (void)viewDidLoad {
218
239
  [super viewDidLoad];
219
240
  self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
@@ -321,6 +342,32 @@ static char TrueSheetAccessibilityWindowPreviousElementsKey;
321
342
  }
322
343
  }
323
344
 
345
+ - (NSArray *)accessibilityGrabberElements {
346
+ if (_grabberView && !_grabberView.hidden) {
347
+ return @[ _grabberView ];
348
+ }
349
+
350
+ // The system grabber is an accessibility element UIKit adds to the presented view
351
+ // (a sibling of our root view). Expose it so VoiceOver can resize or dismiss with it.
352
+ // UIKit also keeps a transparent bottom grabber there, so skip invisible views.
353
+ NSMutableArray *elements = [NSMutableArray array];
354
+ for (UIView *subview in self.presentationController.presentedView.subviews) {
355
+ if (subview != self.view && subview.isAccessibilityElement && !subview.hidden && subview.alpha > 0) {
356
+ [elements addObject:subview];
357
+ }
358
+ }
359
+ return elements;
360
+ }
361
+
362
+ - (BOOL)accessibilityPerformEscape {
363
+ if (!_isPresented || !self.dismissible) {
364
+ return NO;
365
+ }
366
+
367
+ [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
368
+ return YES;
369
+ }
370
+
324
371
  - (void)setAccessibilityContentElement:(UIView *)contentView {
325
372
  BOOL hasAccessibilityFooterElements = [contentView isKindOfClass:[TrueSheetContainerView class]] &&
326
373
  [(TrueSheetContainerView *)contentView hasAccessibilityFooterElements];
@@ -333,12 +380,12 @@ static char TrueSheetAccessibilityWindowPreviousElementsKey;
333
380
  // its accessibilityElements getter recomputes live, so a footer/content subtree
334
381
  // that remounts (e.g. swapping a footer button on a state change) stays
335
382
  // discoverable instead of leaving this snapshot pointing at destroyed views.
336
- NSArray *accessibilityElements;
383
+ NSMutableArray *accessibilityElements = [[self accessibilityGrabberElements] mutableCopy];
337
384
  if (isAccessibilityModal) {
338
385
  NSArray *contentElements = contentView.accessibilityElements;
339
- accessibilityElements = contentElements.count > 0 ? contentElements : @[ contentView ];
386
+ [accessibilityElements addObjectsFromArray:contentElements.count > 0 ? contentElements : @[ contentView ]];
340
387
  } else {
341
- accessibilityElements = @[ contentView ];
388
+ [accessibilityElements addObject:contentView];
342
389
  }
343
390
 
344
391
  self.view.isAccessibilityElement = NO;
@@ -351,6 +398,15 @@ static char TrueSheetAccessibilityWindowPreviousElementsKey;
351
398
  presentedView.accessibilityViewIsModal = isAccessibilityModal;
352
399
  presentedView.accessibilityElements = accessibilityElements;
353
400
 
401
+ // The system grabber is a sibling of our root view, so an escape performed while it
402
+ // is focused walks presentedView → window and never reaches TrueSheetControllerView.
403
+ if (@available(iOS 17.0, *)) {
404
+ __weak __typeof(self) weakSelf = self;
405
+ presentedView.accessibilityPerformEscapeBlock = ^BOOL {
406
+ return [weakSelf accessibilityPerformEscape];
407
+ };
408
+ }
409
+
354
410
  UIWindow *window = self.view.window;
355
411
  UIViewController *presentingViewController = [self accessibilityPresentingViewController];
356
412
  if (window && presentingViewController.view) {
@@ -1147,6 +1203,12 @@ static char TrueSheetAccessibilityWindowPreviousElementsKey;
1147
1203
  _grabberView.onIncrement = nil;
1148
1204
  _grabberView.onDecrement = nil;
1149
1205
  }
1206
+
1207
+ // Only the topmost sheet may refresh, or a parent's prop update would steal the
1208
+ // window accessibility override from the child presented over it.
1209
+ if (_isPresented && self.isTopmostPresentedController) {
1210
+ [self setupAccessibilityContainer];
1211
+ }
1150
1212
  }
1151
1213
 
1152
1214
  - (void)handleGrabberTap {
@@ -91,6 +91,13 @@ static const CGFloat kHitPaddingVertical = 10.0;
91
91
  }
92
92
  }
93
93
 
94
+ // Pad the pill evenly like the system grabber. The view frame itself stays inside
95
+ // the sheet, since the reveal check reads the frame rather than this rect.
96
+ - (CGRect)accessibilityFrame {
97
+ CGRect rect = CGRectInset(_vibrancyView.frame, -kHitPaddingHorizontal, -kHitPaddingVertical);
98
+ return UIAccessibilityConvertFrameToScreenCoordinates(rect, self);
99
+ }
100
+
94
101
  - (void)accessibilityIncrement {
95
102
  if (_onIncrement) {
96
103
  _onIncrement();
@@ -144,14 +151,17 @@ static const CGFloat kHitPaddingVertical = 10.0;
144
151
  CGFloat topMargin = [self effectiveTopMargin];
145
152
  CGFloat parentWidth = self.superview ? self.superview.bounds.size.width : UIScreen.mainScreen.bounds.size.width;
146
153
 
154
+ // Keep the frame inside the sheet. A frame above the top edge reads as a clipped element
155
+ // to VoiceOver, and the sheet reveals it on focus by moving to the next detent.
147
156
  CGFloat frameWidth = pillWidth + kHitPaddingHorizontal * 2;
148
- CGFloat frameHeight = pillHeight + kHitPaddingVertical * 2;
149
- CGFloat frameY = topMargin - kHitPaddingVertical;
157
+ CGFloat frameY = MAX(0, topMargin - kHitPaddingVertical);
158
+ CGFloat pillY = topMargin - frameY;
159
+ CGFloat frameHeight = pillY + pillHeight + kHitPaddingVertical;
150
160
 
151
161
  self.frame = CGRectMake((parentWidth - frameWidth) / 2.0, frameY, frameWidth, frameHeight);
152
162
  self.backgroundColor = UIColor.clearColor;
153
163
 
154
- CGRect pillRect = CGRectMake(kHitPaddingHorizontal, kHitPaddingVertical, pillWidth, pillHeight);
164
+ CGRect pillRect = CGRectMake(kHitPaddingHorizontal, pillY, pillWidth, pillHeight);
155
165
  _vibrancyView.frame = pillRect;
156
166
  _vibrancyView.layer.cornerRadius = [self effectiveCornerRadius];
157
167
  _vibrancyView.clipsToBounds = YES;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lodev09/react-native-true-sheet",
3
- "version": "3.11.13",
3
+ "version": "3.11.14",
4
4
  "description": "The true native bottom sheet experience for your React Native Apps.",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./lib/module/index.js",