自定义弹出视图

本文介绍了一个自定义的iOS模态视图控制器转场动画方案,包括手势驱动的交互式动画效果,如拖动弹出视图进行关闭等。提供了详细的代码实现,帮助开发者更好地理解并应用到实际项目中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


.h文件:

#import <UIKit/UIKit.h>


@interface LHCustomModalTransition : UIPercentDrivenInteractiveTransition<UIViewControllerAnimatedTransitioning,UIViewControllerTransitioningDelegate,UIGestureRecognizerDelegate>

//---设置是否可拖拽(默认:不可拖动)
@property (nonatomic, assign, getter=isDragable) BOOL dragable;
//---初始化 model视图控制器
- (id)initWithModalViewController:(UIViewController *)modalViewController;

@end





.m文件:
#import "LHCustomModalTransition.h"

#define ScreenHeight [[UIScreen mainScreen]bounds].size.height

@interface LHCustomModalTransition()

//---保存入口传入的 试图控制器,用于操作
@property (nonatomic, strong) UIViewController *modalVC;
//---保存时弹出还是收起的状态
@property (nonatomic, assign) BOOL isDismiss;
//---拖动手势
@property (nonatomic, strong) UIPanGestureRecognizer *dragGesture;

//---保存frame
@property (nonatomic, assign) CATransform3D tempTransform;
//---保存当前动画的上下文
@property (nonatomic, strong) id<UIViewControllerContextTransitioning> transitionContext;

//---保存是否可手势驱动
@property BOOL isInteractive;
//---保存是否完成
@property BOOL isDragEnough;
@end

@implementation LHCustomModalTransition

- (instancetype)initWithModalViewController:(UIViewController *)modalViewController
{
    self = [super init];
    if (self) {
        _modalVC = modalViewController;
        _dragable = NO;
    }
    return self;
}

- (void)setDragable:(BOOL)dragable
{
    _dragable = dragable;
    if (self.isDragable)
    {
        self.dragGesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handlePanGesture:)];
        self.dragGesture.delegate = self;
        [self.modalVC.view addGestureRecognizer:self.dragGesture];
    }
}

#pragma mark -处理手势驱动
- (void)handlePanGesture:(UIPanGestureRecognizer *)gesture
{
    CGPoint translation = [gesture translationInView:self.modalVC.view];
    switch (gesture.state) {
        case UIGestureRecognizerStateBegan:{
            self.isInteractive =  YES;
            [self.modalVC dismissViewControllerAnimated:YES completion:nil];
            break;
        }
        case UIGestureRecognizerStateChanged:{
            //---滑动距离/屏幕高度 = 进度
            CGFloat percent = (translation.y/ScreenHeight) <= 1 ? (translation.y/ScreenHeight):1;
            self.isDragEnough = (percent > 0.2);
            [self updateInteractiveTransition:percent];
            
            break;
        }
        case UIGestureRecognizerStateCancelled:
        case UIGestureRecognizerStateEnded:{
            //---拖动结束,判断是执行还是取消
            self.isInteractive = NO;
            if (gesture.state == UIGestureRecognizerStateCancelled || !self.isDragEnough) {
                [self cancelInteractiveTransition];
            }else{
                [self finishInteractiveTransition];
            }
            break;
        }
            
        default:
            break;
    }
}

#pragma mark -设置是否手势驱动转场动画
- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator
{
    return nil;
}

- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator
{
    if (self.isInteractive && self.dragable) {
        self.isDismiss = YES;
        return self;
    }
    return nil;
}

#pragma mark -覆写手势驱动过程
//---开始
- (void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    //---保存当前缩小的状态
    self.tempTransform = toVC.view.layer.transform;
    //---保存transitionContext,下面使用
    self.transitionContext = transitionContext;
}

//---更新
- (void)updateInteractiveTransition:(CGFloat)percentComplete
{
    id<UIViewControllerContextTransitioning> transitionContext = self.transitionContext;
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    //---计算放大倍数,然后进行缩放
    CGFloat scale = 1 + ((1/0.8*0.95) - 1)*percentComplete;
    toVC.view.layer.transform = CATransform3DScale(self.tempTransform, scale, scale, 1);
    
    //---下拉的长度
    CGRect nowFrame = CGRectMake(0,
                                 (CGRectGetHeight(fromVC.view.bounds) * percentComplete),
                                 CGRectGetWidth(fromVC.view.frame),
                                 CGRectGetHeight(fromVC.view.frame));
    fromVC.view.frame = nowFrame;
}

//---完成
- (void)finishInteractiveTransition
{
    id<UIViewControllerContextTransitioning> transitionContext = self.transitionContext;
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    //---收起弹出的试图控制器
    CGRect finalRect = CGRectMake(0,
                                  CGRectGetHeight(fromVC.view.bounds) ,
                                  CGRectGetWidth(fromVC.view.frame),
                                  CGRectGetHeight(fromVC.view.frame));
    
    //---动画
    [UIView animateWithDuration:[self transitionDuration:transitionContext]
                          delay:0
         usingSpringWithDamping:5
          initialSpringVelocity:5
                        options:UIViewAnimationOptionCurveEaseInOut
                     animations:^{
                         
                         //---放大到原始大小
                         CATransform3D transition = CATransform3DIdentity;
                         toVC.view.layer.transform = transition;
                         //---向下移动隐藏
                         fromVC.view.frame = finalRect;

    } completion:^(BOOL finished) {
       //---标记结束
        [transitionContext completeTransition:YES];
        self.modalVC = nil;
    }];
}

//---取消
- (void)cancelInteractiveTransition
{
    id<UIViewControllerContextTransitioning> transitionContext = self.transitionContext;
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    [UIView animateWithDuration:0.4
                          delay:0
         usingSpringWithDamping:5
          initialSpringVelocity:5
                        options:UIViewAnimationOptionCurveEaseOut
                     animations:^{
                         //---缩小到大小
                         toVC.view.layer.transform = self.tempTransform;
                         //---向上移动,恢复到弹出的状态
                         fromVC.view.frame = CGRectMake(0,0,
                                                        CGRectGetWidth(fromVC.view.frame),
                                                        CGRectGetHeight(fromVC.view.frame));
                         
     } completion:^(BOOL finished) {
         //---标记未结束
         [transitionContext completeTransition:NO];
     }];
}

#pragma mark -转场委托实现
//---转场动画时间
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
    return 0.8;
}

//---转场动画具体实现
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    //---获取转场的两个视图控制器
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    //---获取产生转场的容器view
    UIView *containerView = [transitionContext containerView];

    if (!self.isDismiss)//---弹出
    {
        //---解决角度变换时,fromVC有一半会出现在 toVC上的bug
        fromVC.view.layer.zPosition = -400;
        toVC.view.layer.zPosition = 400;
        
        //---初始化弹出视图在底部
        CGRect finalRect = [transitionContext finalFrameForViewController:toVC];
        toVC.view.frame = CGRectOffset(finalRect, 0, ScreenHeight);
        [containerView addSubview:toVC.view];
        
        //---弹出动画
        [UIView animateWithDuration:[self transitionDuration:transitionContext]/2.0 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            
            //---第一步形变:绕X轴旋转并缩小
            [fromVC.view.layer setTransform:[self firstTransform]];

        } completion:^(BOOL finished) {
            
            [UIView animateWithDuration:[self transitionDuration:transitionContext]/2.0 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{

                //---第二步形变:向上移动,并缩小
                [fromVC.view.layer setTransform:[self secondTransform]];
                
            } completion:^(BOOL finished) {
            }];
            
        }];
        
        [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            
            //---弹出视图控制器
            toVC.view.frame = finalRect;
            
        } completion:^(BOOL finished) {
            //---标记结束
            [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
        }];
        
    }
    else //---收起
    {
        
        //---获得当前frame
        CGRect initRect = [transitionContext initialFrameForViewController:fromVC];
        CGRect finalRect = CGRectOffset(initRect, 0, ScreenHeight);
        
        //---收起动画
        [UIView animateWithDuration:[self transitionDuration:transitionContext]/2.0 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            //---第一步形变:绕X轴旋转并缩小
            [toVC.view.layer setTransform:[self firstTransform]];
          
        } completion:^(BOOL finished) {
            
            [UIView animateWithDuration:[self transitionDuration:transitionContext]/2.0 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
                //---第二步形变:回到初始位置
                [toVC.view.layer setTransform:CATransform3DIdentity];
   
            } completion:^(BOOL finished) {
            }];
            
        }];
        
        [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            
            //---收起视图控制器
            fromVC.view.frame = finalRect;
            
        } completion:^(BOOL finished) {
            //---标记结束
            [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
        }];
        
    }
}

//---弹出动画所使用协议
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
    self.isDismiss = NO;
    return self;
}

//---收起动画所用协议
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
    self.isDismiss = YES;
    return self;
}

#pragma mark - 变换操作
//---第一步变换
- (CATransform3D )firstTransform
{
    CATransform3D transform = CATransform3DIdentity;
    transform.m34 = 1.0/ -900;
    //---宽高缩小0.9
    transform = CATransform3DScale(transform, 0.95, 0.95, 1);
    //---绕X轴旋转15度
    transform = CATransform3DRotate(transform, 15.0 * M_PI / 180.0 , 1, 0, 0);
    
    return transform;
}

//---第二步变换
- (CATransform3D )secondTransform
{
    CATransform3D transform = CATransform3DIdentity;
    transform.m34 = [self firstTransform].m34;
    //---向上移动的高度
    transform = CATransform3DTranslate(transform, 0, -20 , 0);
    //---宽高缩小0.8
    transform = CATransform3DScale(transform, 0.8, 0.8, 1);
    
    return transform;
}

@end

使用:

//---初始化要弹出跳转的视图控制器
    ModalViewController *modalVC = [ModalViewController new];
    //---必须强引用,否则会被释放,自定义dismiss的转场无效
    self.transition = [[LHCustomModalTransition alloc]initWithModalViewController:modalVC];
    self.transition.dragable = YES;//---是否可下拉收起
    modalVC.transitioningDelegate = self.transition;
    //---必须添加这句.自定义转场动画
    modalVC.modalPresentationStyle = UIModalPresentationCustom;

    [self presentViewController:modalVC animated:YES completion:nil];



资源下载链接为: https://pan.quark.cn/s/9e7ef05254f8 行列式是线性代数的核心概念,在求解线性方程组、分析矩阵特性以及几何计算中都极为关键。本教程将讲解如何用C++实现行列式的计算,重点在于如何输出分数形式的结果。 行列式定义如下:对于n阶方阵A=(a_ij),其行列式由主对角线元素的乘积,按行或列的奇偶性赋予正负号后求和得到,记作det(A)。例如,2×2矩阵的行列式为det(A)=a11×a22-a12×a21,而更高阶矩阵的行列式可通过Laplace展开或Sarrus规则递归计算。 在C++中实现行列式计算时,首先需定义矩阵类或结构体,用二维数组存储矩阵元素,并实现初始化、加法、乘法、转置等操作。为支持分数形式输出,需引入分数类,包含分子和分母两个整数,并提供与整数、浮点数的转换以及加、减、乘、除等运算。C++中可借助std::pair表示分数,或自定义结构体并重载运算符。 计算行列式的函数实现上,3×3及以下矩阵可直接按定义计算,更大矩阵可采用Laplace展开或高斯 - 约旦消元法。Laplace展开是沿某行或列展开,将矩阵分解为多个小矩阵的行列式乘积,再递归计算。在处理分数输出时,需注意避免无限循环和除零错误,如在分数运算前先约简,确保分子分母互质,且所有计算基于整数进行,最后再转为浮点数,以避免浮点数误差。 为提升代码可读性和可维护性,建议采用面向对象编程,将矩阵类和分数类封装,每个类有明确功能和接口,便于后续扩展如矩阵求逆、计算特征值等功能。 总结C++实现行列式计算的关键步骤:一是定义矩阵类和分数类;二是实现矩阵基本操作;三是设计行列式计算函数;四是用分数类处理精确计算;五是编写测试用例验证程序正确性。通过这些步骤,可构建一个高效准确的行列式计算程序,支持分数形式计算,为C++编程和线性代数应用奠定基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值