如何在Objective-C中为ViewController设置Detents值及自定义弹出样式
实现从底部弹出、占屏幕2/3高度且带模糊背景的ViewController(Objective-C)
要实现这个效果,仅靠系统默认的modalPresentationStyle无法满足需求,推荐使用自定义UIPresentationController+转场动画的方式,这是iOS官方推荐的自定义弹窗方案,性能和兼容性更优。
步骤1:自定义PresentationController(控制弹窗布局与背景)
这个类负责添加模糊背景、设置弹窗的尺寸位置,以及处理背景交互。
#import <UIKit/UIKit.h> @interface CustomBottomPresentationController : UIPresentationController @end @implementation CustomBottomPresentationController - (void)presentationTransitionWillBegin { // 添加模糊背景视图 UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleSystemMaterial]; UIVisualEffectView *blurView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; blurView.frame = self.containerView.bounds; blurView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [self.containerView insertSubview:blurView atIndex:0]; // 给背景添加点击关闭手势 UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleBackgroundTap)]; [blurView addGestureRecognizer:tapGesture]; // 背景淡入动画 blurView.alpha = 0.0; id<UIViewControllerTransitionCoordinator> coordinator = self.presentingViewController.transitionCoordinator; [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { blurView.alpha = 1.0; } completion:nil]; } - (void)dismissalTransitionWillBegin { // 背景淡出动画 id<UIViewControllerTransitionCoordinator> coordinator = self.presentingViewController.transitionCoordinator; [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { self.containerView.subviews.firstObject.alpha = 0.0; } completion:nil]; } - (CGRect)frameOfPresentedViewInContainerView { // 设置弹窗高度为屏幕的2/3,从底部开始布局 CGRect containerBounds = self.containerView.bounds; CGFloat popupHeight = containerBounds.size.height * 2.0 / 3.0; return CGRectMake(0, containerBounds.size.height - popupHeight, containerBounds.size.width, popupHeight); } - (void)handleBackgroundTap { [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; } @end
步骤2:自定义转场动画控制器(控制弹窗滑入滑出效果)
这个类负责实现弹窗从底部滑入、滑回底部的动画逻辑。
#import <UIKit/UIKit.h> @interface CustomBottomTransitionAnimator : NSObject <UIViewControllerAnimatedTransitioning> @property (nonatomic, assign) BOOL isPresenting; // 标记是弹出还是消失动画 @end @implementation CustomBottomTransitionAnimator - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext { return 0.35; // 动画时长 } - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext { if (self.isPresenting) { // 弹出动画:从底部滑入 UIViewController *presentedVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; UIView *presentedView = presentedVC.view; presentedView.frame = [transitionContext finalFrameForViewController:presentedVC]; // 初始状态:弹窗在屏幕下方 presentedView.transform = CGAffineTransformMakeTranslation(0, presentedView.bounds.size.height); [transitionContext.containerView addSubview:presentedView]; [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:0.8 initialSpringVelocity:0.8 options:UIViewAnimationOptionCurveEaseInOut animations:^{ presentedView.transform = CGAffineTransformIdentity; } completion:^(BOOL finished) { [transitionContext completeTransition:YES]; }]; } else { // 消失动画:滑回底部 UIViewController *dismissedVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; UIView *dismissedView = dismissedVC.view; [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ dismissedView.transform = CGAffineTransformMakeTranslation(0, dismissedView.bounds.size.height); } completion:^(BOOL finished) { [dismissedView removeFromSuperview]; [transitionContext completeTransition:YES]; }]; } } @end
步骤3:在父控制器中触发弹窗
在需要弹出弹窗的控制器中,实现UIViewControllerTransitioningDelegate协议,关联自定义的PresentationController和动画控制器。
// 父控制器的.h文件 #import <UIKit/UIKit.h> @interface ParentViewController : UIViewController <UIViewControllerTransitioningDelegate> @end // 父控制器的.m文件 @implementation ParentViewController // 弹出按钮的点击事件 - (IBAction)showBottomPopup:(id)sender { // 初始化你要弹出的ViewController(这里假设用xib创建) BottomPopupViewController *popupVC = [[BottomPopupViewController alloc] initWithNibName:@"BottomPopupViewController" bundle:nil]; // 设置自定义弹窗样式 popupVC.modalPresentationStyle = UIModalPresentationCustom; popupVC.transitioningDelegate = self; [self presentViewController:popupVC animated:YES completion:nil]; } // 返回自定义PresentationController - (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source { return [[CustomBottomPresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting]; } // 返回弹出动画控制器 - (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { CustomBottomTransitionAnimator *animator = [[CustomBottomTransitionAnimator alloc] init]; animator.isPresenting = YES; return animator; } // 返回消失动画控制器 - (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed { CustomBottomTransitionAnimator *animator = [[CustomBottomTransitionAnimator alloc] init]; animator.isPresenting = NO; return animator; } @end
自定义调整说明
- 模糊风格:修改
UIBlurEffectStyle可以切换背景模糊效果,比如UIBlurEffectStyleDark、UIBlurEffectStyleLight; - 弹窗高度:在
frameOfPresentedViewInContainerView方法中调整popupHeight的比例即可; - 动画效果:可以修改动画时长、弹簧阻尼等参数,调整弹窗的滑入滑出质感;
- 背景交互:如果不需要点击背景关闭弹窗,删除
handleBackgroundTap方法和手势即可。
内容的提问来源于stack exchange,提问作者Jijin Sundar
相关产品推荐
相关产品推荐

