封装了一个优雅的iOS转场动画

news2024/11/18 9:27:42

效果图

请添加图片描述

代码

//
//  LBTransition.m
//  LBWaterFallLayout_Example
//
//  Created by mac on 2024/6/16.
//  Copyright © 2024 liuboliu. All rights reserved.
//

#import "LBTransition.h"

@interface LBPushAnimation:NSObject<UIViewControllerAnimatedTransitioning>

@property (nonatomic, weak) id <LBTransitionDelegate> delegate;

@end

@implementation LBPushAnimation

- (instancetype)initWithDelegate:(id <LBTransitionDelegate>) delegate
{
    if (self = [super init]) {
        self.delegate = delegate;
    }
    return self;
}

#pragma mark - UIViewControllerCanimatedtransitioning

- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext{
    return 0.25;
}

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    UIView *containerView = [transitionContext containerView];
    UIViewController *toVC = (UIViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    
    [containerView addSubview:toView];
    
    UIViewController <LBTransitionDelegate> *transToVc;
    
    if ([toVC conformsToProtocol:@protocol(LBTransitionDelegate)]) {
        transToVc = (UIViewController <LBTransitionDelegate> *)toVC;
    }
    
    id transmitData = [self.delegate transmitViewData];
    CGRect finalFrame = [transToVc transmitViewfinalFrameWithData:transmitData containerView:containerView];
    UIView *transView = [self.delegate prepareTransimitView:containerView finalFrame:finalFrame];
    toView.alpha = 0;
    CGFloat scaleWidth = transView.frame.size.width / toView.frame.size.width;
    CGFloat scaleHeight = transView.frame.size.height / toView.frame.size.height;
    toView.transform = CGAffineTransformMakeScale(scaleWidth, scaleHeight);
    toView.center = transView.center;
    CGRect toViewFinalFrame = [transitionContext finalFrameForViewController:toVC];
    CGPoint finalCenter = CGPointMake(toViewFinalFrame.origin.x + toViewFinalFrame.size.width * 0.5,
                                      toViewFinalFrame.origin.y + toViewFinalFrame.size.height * 0.5);
    NSTimeInterval duration = [self transitionDuration:transitionContext];
    [UIView animateWithDuration:duration
                          delay:0
                        options:UIViewAnimationOptionCurveEaseInOut
                     animations:^{
        [transToVc makeupAnimationStateTransmitView:transView
                                               data:transmitData
                                      containerView:containerView];
        toView.alpha = 1;
        toView.center = finalCenter;
        toView.transform = CGAffineTransformIdentity;
    } completion:^(BOOL finished) {
        [self.delegate completeTransition];
        [transToVc completeTransitionWithView:transView data:transmitData];
        [transitionContext completeTransition:!transitionContext.transitionWasCancelled];
    }];
    
}

@end

@interface LBPopAnimatin : NSObject <UIViewControllerAnimatedTransitioning>

@property (nonatomic, weak) id <LBTransitionDelegate> delegate;

@end

@implementation LBPopAnimatin

- (instancetype)initWithDelegate:(id <LBTransitionDelegate>)delegate {
    if (self = [super init]) {
        self.delegate = delegate;
    }
    return self;
}

#pragma mark - UIViewControllerAnimatedTransitioning

- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
    return 0.25;
}

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    UIView *containerView = [transitionContext containerView];
    UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIView *fromView = fromVC.view;
    [containerView insertSubview:toView aboveSubview:fromView];
    
    UIViewController <LBTransitionDelegate> *transToVc;
    if ([fromVC conformsToProtocol:@protocol(LBTransitionDelegate)]) {
        transToVc = (UIViewController <LBTransitionDelegate> *)fromVC;
    }
    id transmitData = [transToVc transmitViewData];
    
    CGRect finalFrame = [self.delegate transmitViewfinalFrameWithData:transmitData containerView:containerView];
    UIView *transView = [transToVc prepareTransimitView:containerView finalFrame:finalFrame];
    
    NSTimeInterval duration = [self transitionDuration:transitionContext];
    
    CGPoint finalCenter = CGPointMake(finalFrame.origin.x + finalFrame.size.width * 0.5, finalFrame.origin.y + finalFrame.size.height * 0.5);
    
    CGFloat scaleWidth = finalFrame.size.width / fromView.frame.size.width;
    CGFloat scaleHeight = finalFrame.size.height / fromView.frame.size.height;
    
    CGFloat scale = scaleWidth < scaleHeight ? scaleWidth : scaleHeight;
    CGAffineTransform transform = CGAffineTransformMakeScale(scale, scale);
    
    [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        [self.delegate makeupAnimationStateTransmitView:transView 
                                                   data:transmitData
                                          containerView:containerView];
        fromView.alpha = 0;
        fromView.center = finalCenter;
        fromView.transform = transform;
    } completion:^(BOOL finished) {
        if (transitionContext.transitionWasCancelled) {
            [transToVc completeTransitionWithView:transView data:transmitData];
        } else {
            [self.delegate completeTransitionWithView:transView data:transmitData];
        }
        [transitionContext completeTransition:!transitionContext.transitionWasCancelled];
    }];
}

@end

@interface LBTransition () <UIGestureRecognizerDelegate, UIViewControllerTransitioningDelegate>

@property (nonatomic, weak) UIViewController <LBTransitionDelegate> *presentVC;
@property (nonatomic, weak) id <UIViewControllerContextTransitioning> transitionContext;
@property (nonatomic, assign) CGPoint startLocation;
@property (nonatomic, assign) CGFloat animationDuration;
@property (nonatomic, strong) UIView *transView;
@property (nonatomic, assign) CGFloat transViewCornerRadius;
@property (nonatomic, strong) id transmidData;
@property (nonatomic, strong) UIView *snapshotView;
@property (nonatomic, assign) CGRect origionFrame;
@property (nonatomic, assign) CGRect transViewFrame;

@end

@implementation LBTransition

- (instancetype)initWithDelegate:(id<LBTransitionDelegate>)delegate
{
    if (self = [super init]) {
        self.delegate = delegate;
        self.origionFrame = CGRectZero;
        self.transViewFrame = CGRectZero;
        self.animationDuration = 0.25;
    }
    return self;
}

#pragma mark - UINaivgationControllerDelegate

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController 
                                  animationControllerForOperation:(UINavigationControllerOperation)operation
                                               fromViewController:(UIViewController *)fromVC
                                                 toViewController:(UIViewController *)toVC
{
    if (operation == UINavigationControllerOperationPush) {
        return [[LBPushAnimation alloc] initWithDelegate:self.delegate];
    } else {
        return [[LBPopAnimatin alloc] initWithDelegate:self.delegate];
    }
}


- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
                         interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController
{
    if (!self.interacting) {
        return nil;
    }
    return self;
}

- (void)wireToViewController:(UIViewController<LBTransitionDelegate> *)viewController
{
    self.presentVC = viewController;
    UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
    gesture.delegate = self;
    [viewController.view addGestureRecognizer:gesture];
    self.gesture = gesture;
    
}

- (void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    if (self.gesture.state == UIGestureRecognizerStatePossible) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [transitionContext completeTransition:NO];
        });
        return;
    }
    self.transitionContext = transitionContext;
    UIView *containerView = [transitionContext containerView];
    
    UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIView *fromView = fromVC.view;
    [containerView insertSubview:toView belowSubview:fromView];
    self.transmidData = [self.presentVC transmitViewData];
    CGRect finalFrame = [self.delegate transmitViewfinalFrameWithData:self.transmidData containerView:containerView];
    self.transView = [self.presentVC prepareTransimitView:containerView finalFrame:finalFrame];
    self.transViewCornerRadius = self.transView.layer.cornerRadius;
    self.snapshotView = [fromView snapshotViewAfterScreenUpdates:NO];
    [self.snapshotView addSubview:self.transView];
    self.origionFrame = self.snapshotView.frame;
    self.transViewFrame = self.transView.frame;
    [containerView addSubview:self.snapshotView];
    fromView.alpha = 0;
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    return YES;
}
#pragma mark - action

- (void)handleGesture:(UIPanGestureRecognizer *)gestureRecognizer
{
    CGFloat width = gestureRecognizer.view.superview.frame.size.width;
    CGPoint location = [gestureRecognizer locationInView:gestureRecognizer.view.superview];
    switch (gestureRecognizer.state) {
        case UIGestureRecognizerStateBegan:
        {
            self.interacting = YES;
            id transmitData = [self.presentVC transmitViewData];
            [self.delegate prepareAnimationWithData:transmitData];
            self.startLocation = location;
            UINavigationController *navigationController = self.presentVC.navigationController;
            id <UINavigationControllerDelegate> navigationControllerDelegate = navigationController.delegate;
            navigationController.delegate = self;
            [self.presentVC.navigationController popViewControllerAnimated:YES];
            navigationController.delegate = navigationControllerDelegate;
        }
            break;
            
        case UIGestureRecognizerStateChanged: 
        {
            CGPoint trans = CGPointMake(location.x - self.startLocation.x, location.y - self.startLocation.y);
            CGFloat minScale = 0.3;
            CGFloat scale = (width - fabs(trans.x))/ width * (1 - minScale) + minScale;
            
            CGPoint transViewCenter = CGPointMake(self.transViewFrame.origin.x + self.transViewFrame.size.width * 0.5, self.transViewFrame.origin.y + self.transViewFrame.origin.y + self.transViewFrame.size.height * 0.5);
            
            self.snapshotView.frame = CGRectMake(0, 0, self.origionFrame.size.width * scale, self.origionFrame.size.height * scale);
            self.snapshotView.center = CGPointMake(self.origionFrame.size.width * 0.5 + trans.x, self.origionFrame.size.height * 0.5 + trans.y);
            transViewCenter.x *= scale;
            transViewCenter.y *= scale;
            self.transView.frame = CGRectMake(0, 0, self.transViewFrame.size.width * scale, self.transViewFrame.size.height * scale);
            self.transView.layer.cornerRadius = self.transViewCornerRadius * scale;
            self.transView.center = transViewCenter;
        }
            break;
        case UIGestureRecognizerStateCancelled:
        case UIGestureRecognizerStateEnded:
        {
            UIView *containerView = [self.transitionContext containerView];
            UIView *toView = [self.transitionContext viewForKey:UITransitionContextToViewKey];
            CGPoint trans = CGPointMake(location.x - self.startLocation.x, location.y - self.startLocation.y);
            if (fabs(trans.x) < (width * 0.15)) {
                [UIView animateWithDuration:self.animationDuration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
                    self.snapshotView.frame = self.origionFrame;
                    self.transView.frame = self.transViewFrame;
                    self.transView.layer.cornerRadius = 12;
                } completion:^(BOOL finished) {
                    [toView removeFromSuperview];
                    self.presentVC.view.alpha = 1;
                    [self.snapshotView removeFromSuperview];
                    [self.presentVC completeTransitionWithView:self.transView data:self.transmidData];
                    [self.delegate completeTransition];
                    self.interacting = NO;
                    [self cancelInteractiveTransition];
                    [self.transitionContext completeTransition:NO];
                }];
            } else {
                CGRect cFrame = [self.snapshotView convertRect:self.transView.frame toView:containerView];
                [containerView addSubview:self.transView];
                
                self.transView.frame = cFrame;
                NSTimeInterval duration = self.animationDuration;
                [UIView animateKeyframesWithDuration:duration delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
                    [UIView addKeyframeWithRelativeStartTime:0 relativeDuration:0.1 animations:^{
                        self.snapshotView.alpha = 0;
                    }];
                    [UIView addKeyframeWithRelativeStartTime:0.1 relativeDuration:1 animations:^{
                        [self.delegate makeupAnimationStateTransmitView:self.transView
                                                                   data:self.transmidData
                                                          containerView:containerView];
                    }];
                } completion:^(BOOL finished) {
                    [self.snapshotView removeFromSuperview];
                    [self.delegate completeTransitionWithView:self.transView data:self.transmidData];
                    self.interacting = NO;
                    [self finishInteractiveTransition];
                    [self.transitionContext completeTransition:YES];
                }];
            }
            break;
        }
        default:
            break;
    }
}
@end

```![请添加图片描述](https://img-blog.csdnimg.cn/direct/c1e3f47c8be74f6d95a9062735b2d36e.gif)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1858657.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Halcon 如何对区域进行交集,补集,反选,合并操作

1 Intersection交集 dev_open_window(0,0,512,512,black,WindowHandle)gen_circle(Circle1,114.5,127.5,89.3588)gen_circle(Circle2,163.5,171.5,94.8472)intersection(Circle1,Circle2,RegionIntersection)dev_clear_window()dev_display(RegionIntersection)2 Differece 补集…

人机恋爱新趋势:与AI男友谈恋爱的甜蜜与挑战

"我曾经把ChatGPT当成工具&#xff0c;从未追过星&#xff0c;也没有嗑过CP。没想到&#xff0c;到了36岁&#xff0c;我竟然嗑上了AI男友。Open AI&#xff0c;你赢了。你不仅是最好的AI公司&#xff0c;还是乙女游戏公司。" 转行大龄互联网人&#xff0c;走遍20国…

相位和展开相位

相位 (Phase) 相位是一个周期信号在一个周期内的位置&#xff0c;通常以角度&#xff08;度或弧度&#xff09;表示。在许多应用中&#xff0c;相位被限制在一个周期内。例如&#xff0c;相位通常被限定在 −180∘到 180∘ 或 0∘ 到 360∘ 之间。 示例 −90∘ 表示信号在周…

【网络协议】精讲ARP协议工作原理!图解超赞超详细!!!

亲爱的用户&#xff0c;打开微信&#xff0c;搜索公众号&#xff1a;“风云说通信”&#xff0c;即可免费阅读该文章~~ 目录 前言 1. ARP协议介绍 1.1 ARP协议功能 1.2 ARP请求报文 1.3 ARP工作原理 2. ARP 缓存超时 2.1 RARP 3. ARP 攻击 3.1 ARP 攻击分类 前言 首先…

【Linux】—Apache Hive 安装部署

文章目录 前言认识Metadata认识Metastoremetastore三种配置方式 一、安装前准备二、下载hive-3.1.2安装包三、下载完成后&#xff0c;通过xftp6上传到Linux服务器上四、解压Hive安装包五、配置Hive六、内嵌模型安装—Hive元数据配置到Derby七、本地模式安装—Hive元数据配置到M…

Linux:基础IO(三.软硬链接、动态库和静态库、动精态库的制作和加载)

上次介绍了基础IO&#xff08;二&#xff09;&#xff1a;Linux&#xff1a;基础IO&#xff08;二.缓冲区、模拟一下缓冲区、详细讲解文件系统&#xff09; 文章目录 1.软硬链接1.1硬链接1.2软链接使用场景 2.动态库和静态库1.1回顾1.2静态库的制作和使用为什么要有库制作者角度…

基于单片机的智能浇花系统设计与实现

摘要: 设计了一种智能湿度感应浇花系统 。 系统以单片机 AT89S52 为控制芯片&#xff0c;利用 SLHT5-1 土壤湿度传感器来检测土壤的相对湿度&#xff0c;再通过单片机进行信息处理&#xff0c;采用模糊控制方法&#xff0c;输出控制信号&#xff0c;控制继电器的动作&…

[RPI4] 树莓派4b安装istoreos及使用 -- 1. 系统安装

最近在研究家庭智能化的一些东西,其中包括网络,智能家居等一系列内容,然后看过的资料有的想再回来看的时候就找不到了,然后就想着开这么一个系列,做一些记录,先从智能家居开始吧。 1 安装istoreos系统 iStoreOS 目标是提供一个人人会用的路由兼轻 NAS 系统,不管是作为路…

MES管理系统中的仓库管理功能有哪些用途

在当今制造业迅猛发展的背景下&#xff0c;企业对于车间生产调度的需求日益迫切。为此&#xff0c;MES管理系统应运而生&#xff0c;它作为一款专注于车间生产调度的管理信息系统&#xff0c;正逐步成为制造业提升生产效率、优化资源配置的利器。特别是其在仓储和物流管理方面的…

算法社区-从零开始构建(一)

好久没动笔了&#xff0c;一是要处理的东西很多&#xff0c;二则写出来未见得深刻&#xff0c;感觉沉淀得不够&#xff0c;太浅显的东西就没必要分享。 正好最近在研究算法层面的东西&#xff0c;感觉挺受用的&#xff0c;就想着把这些东西整理出来&#xff0c;有点像社区的雏形…

NAPI篇【4】——NAPI应用点亮一个LED

OpenHarmony的NAPI功能为开发者提供了JS与C/C不同语言模块之间的相互访问&#xff0c;交互的能力&#xff0c;使得开发者使用C或者C语言实现应用的关键功能。如操作开发板中某个GPIO节点的状态&#xff08;OpenHarmony并没有提供直接操作GPIO口状态的API&#xff09;&#xff0…

Vue-Cli 创建vue2.0 + TypeScript 项目

这里写目录标题 一、创建项目名称二、选择 Manually select features三、勾选配置项四、选择vue版本五、其它配置 一、创建项目名称 vue create 项目名称&#xff08;项目名字不能含义大写字母&#xff09;二、选择 Manually select features &#xff08;按箭头上下进行移动…

万物皆对象,你信吗?

**内存空间和数据都消失&#xff0c;数据怎么会消失的&#xff1f;**空间没了&#xff0c;数据自然也跟着消失。因为数据就是在空间里面的。就像宇宙大爆炸&#xff0c;我们还能存在嘛&#xff0c;是不是已经undefined了。「一块小内存上有2种数据类型」 内部存储的数据 地址值…

使用Fiddler如何创造大量数据

在调试和分析网络流量时&#xff0c;您是否曾为无法深入了解请求和响应的数据而感到困惑&#xff1f;如果有一种工具可以帮助您轻松抓取和分析网络流量&#xff0c;您的工作效率将大大提升。Fiddler正是这样一款功能强大的抓包工具&#xff0c;广受开发者和测试人员的青睐。 Fi…

专业140+总分400+武汉理工大学855信号与系统考研经验电子信息与通信工程,真题,大纲,参考书

专业855信号与系统140&#xff0c;总分400&#xff0c;今年顺利上岸武汉理工大学&#xff0c;总结一下自己的复习经历&#xff0c;希望对报考武理工的同学有所帮助。专业课&#xff1a;855信号与系统 首先教材&#xff1a; 《信号与系统》高等教育出版社 作者&#xff1a;刘泉…

详解三种常用标准化 Batch Norm Layer Norm RMSNorm

参考&#xff1a; BN究竟起了什么作用&#xff1f;一个闭门造车的分析《动手学深度学习》7.5 节 深度学习中&#xff0c;归一化是常用的稳定训练的手段&#xff0c;CV 中常用 Batch Norm&#xff1b; Transformer 类模型中常用 layer norm&#xff0c;而 RMSNorm 是近期很流行…

Pyppeteer原理介绍和入门尝试

pyppeteer仓库地址&#xff1a;https://github.com/miyakogi/pyppeteer puppeteer仓库地址&#xff1a;https://github.com/search?qpuppeteer&typerepositories 因为有些网页是可以检测到是否是使用了selenium。并且selenium所谓的保护机制不允许跨域cookies保存以及登…

JavaScript的学习之事件的简介

目录 一、事件是什么 二、如何处理事件 一、事件是什么 定义&#xff1a;事件就是浏览器和用户之间的交互行为。 例如&#xff1a;点击按钮、鼠标移动、关闭窗口等。 二、如何处理事件 我们可以在对应的事件属性中设置一些JS行为&#xff0c;当事件触发的时候会将这些代码执行…

java的输入流FileInput Stream类

一、定义 使用InputStream类的FileInputStream子类实现文本文件内容的读取。 二、常用构造方法 三、使用FileInput Stream类按多字节读取数据 1.示例 2、分析 四、常见错误 今天的总结就到这里啦&#xff0c;拜拜&#xff01;

Windows操作系统安装mysql数据库(zip安装包)

MySQL是目前最为流行的开放源码的数据库&#xff0c;是完全网络化的跨平台的关系型数据库系统&#xff0c;它是由瑞典MySQLAB公司开发&#xff0c;目前属于Oracle公司。任何人都能从Internet下载MySQL软件&#xff0c;而无需支付任费用&#xff0c;并且“开放源码”意味着任何人…