Objective-C:如何禁用模态视图的上滑回弹效果
如何禁用iOS PageSheet模态视图的上滑回弹效果
问题场景
我通过以下代码以UIModalPresentationPageSheet样式展示包含CollectionView的导航控制器,并设置了占屏幕60%高度的自定义detent:
GridMenuViewController* vc = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"GridMenuViewControllerID"]; GridMenuNavigationController *nav = [[GridMenuNavigationController alloc] initWithRootViewController:vc]; vc.sheetPresentationController.prefersScrollingExpandsWhenScrolledToEdge = NO; nav.modalPresentationStyle = UIModalPresentationPageSheet; if (@available(iOS 16.0, *)) { if (nav.sheetPresentationController) { id detent = [UISheetPresentationControllerDetent customDetentWithIdentifier:UISheetPresentationControllerDetentIdentifierLarge resolver:^CGFloat(id<UISheetPresentationControllerDetentResolutionContext> _Nonnull context) { return [[UIScreen mainScreen] bounds].size.height * 0.6; }]; nav.sheetPresentationController.detents = @[detent]; nav.sheetPresentationController.prefersGrabberVisible = YES; } } nav.sheetPresentationController.prefersScrollingExpandsWhenScrolledToEdge = NO; [self presentViewController:nav animated:YES completion:nil];
我需要完全禁用该模态视图上滑时的回弹效果,但尝试以下方法均未生效:
- 调整CollectionView的
contentInsetAdjustmentBehavior:
if (@available(iOS 11.0, *)) { self.collectionView.contentInsetAdjustmentBehavior = 2; } else { self.collectionView.automaticallyAdjustsScrollIndicatorInsets = NO; }
- 设置滚动指示器内边距:
self.collectionView.verticalScrollIndicatorInsets = UIEdgeInsetsMake(0, 0, self.view.safeAreaInsets.bottom, 0);
- 在
scrollViewDidScroll强制固定contentOffset:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { [scrollView setContentOffset: CGPointMake(scrollView.contentOffset.x, 0)]; }
解决方案
你遇到的回弹并非CollectionView的滚动回弹,而是PageSheet容器本身的拖拽回弹行为,需针对Sheet控制器进行处理:
方案1:完全禁用Sheet的拖拽交互
如果不需要用户拖拽调整Sheet高度,可直接禁用拖拽:
if (@available(iOS 16.0, *)) { nav.sheetPresentationController.prefersScrollingExpandsWhenScrolledToEdge = NO; nav.sheetPresentationController.isInteractive = NO; // 禁用Sheet的拖拽交互 }
方案2:保留拖拽但禁止回弹(灵活方案)
若需保留拖拽但禁止超出自定义detent的回弹,需通过代理和手势拦截实现:
- 给导航控制器设置Sheet代理:
nav.sheetPresentationController.delegate = self;
- 实现代理方法锁定detent位置:
#pragma mark - UISheetPresentationControllerDelegate - (void)sheetPresentationControllerDidChangeSelectedDetentIdentifier:(UISheetPresentationController *)sheetPresentationController API_AVAILABLE(ios(16.0)) { if (![sheetPresentationController.selectedDetentIdentifier isEqualToString:UISheetPresentationControllerDetentIdentifierLarge]) { [sheetPresentationController setSelectedDetentIdentifier:UISheetPresentationControllerDetentIdentifierLarge animated:NO]; } }
- 拦截拖拽手势限制上滑范围:
- (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if (@available(iOS 16.0, *)) { for (UIGestureRecognizer *gesture in self.navigationController.sheetPresentationController.presentingViewController.view.gestureRecognizers) { if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) { [(UIPanGestureRecognizer *)gesture addTarget:self action:@selector(handleSheetPanGesture:)]; } } } } - (void)handleSheetPanGesture:(UIPanGestureRecognizer *)gesture { if (@available(iOS 16.0, *)) { UISheetPresentationController *sheetPC = self.navigationController.sheetPresentationController; CGPoint translation = [gesture translationInView:gesture.view]; // 禁止向上拖拽超出当前detent位置 if (translation.y < 0) { gesture.enabled = NO; gesture.enabled = YES; [sheetPC setSelectedDetentIdentifier:UISheetPresentationControllerDetentIdentifierLarge animated:NO]; } } }
额外处理:禁用CollectionView自身回弹
如果CollectionView本身存在滚动回弹,需补充设置:
// 完全禁用CollectionView滚动回弹 self.collectionView.bounces = NO; // 或仅禁用顶部回弹 self.collectionView.alwaysBounceVertical = NO;
无效原因说明
你之前的代码均针对CollectionView的滚动属性调整,但问题核心是PageSheet容器的拖拽回弹,而非CollectionView自身滚动,因此这些设置无法解决问题。
内容的提问来源于stack exchange,提问作者Satoshi
相关产品推荐
相关产品推荐

