You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 18:51:16