iOS 转场动画_NAV_图片缩放效果

#import "ZJNavTransition.h"

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

@interface ZJNavTransition()

/** 判断转场同**/
@property (nonatomic, assign) ZJNavTransitionType type;
/** 缩放图片**/
@property (nonatomic, strong) UIImage *image;

@end

/** 小图的原始位置**/
static NSIndexPath *initIndexPath;
/** 小图的返回位置**/
static NSIndexPath *returnIndexPath;
/** 小图的原始大小**/
static CGRect initFrame;
/** 小图的collectionview视图**/
static UICollectionView *smallCollectionView;
/** 一行有几个**/
static NSInteger rowItemCount = 4;
/** 导航栏高度如果没有导航栏这就填0**/
static NSInteger NAVHeight = 64;

@implementation ZJNavTransition

+ (instancetype)transitionWithType:(ZJNavTransitionType)type WithImage:(UIImage *)image
{
    return [[self alloc] initTransitionWithType:type WithImage:image];
    
}

- (instancetype)initTransitionWithType:(ZJNavTransitionType)type WithImage:(UIImage *)image
{
    if (self = [super init])
    {
        _type = type;
        _image = image;
    }
    
    return self;
}

//动画时间
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext
{
    return 0.5;
}

//执行哪一种动画
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
    switch (_type) {
        case ZJNavTransitionPush:
            [self doPushAnimateTransition:transitionContext];
            break;
        case ZJNavTransitionPop:
            [self doPopAnimateTransition:transitionContext];
            break;
    }
}

- (void)doPushAnimateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
    //拿到两个控制器
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    //找到CollectionView
    for (UICollectionView *view in fromVC.view.subviews)
    {
        if ([view isKindOfClass:[UICollectionView class]])
        {
            smallCollectionView = view;
        }
    }
    initIndexPath = [smallCollectionView indexPathsForSelectedItems][0];
    UICollectionViewCell *cell = [smallCollectionView cellForItemAtIndexPath:initIndexPath];
    
    //获取过渡用的视图
    UIView *containerView = [transitionContext containerView];
    UIImageView *imageView = [[UIImageView alloc] init];
    
    [containerView addSubview:toVC.view];
    [containerView addSubview:imageView];
    
    //设置动画前各个控件的状态
    fromVC.view.hidden = YES;
    toVC.view.alpha = 0;
    for (UIView *view in toVC.view.subviews)
    {
        view.hidden = YES;
    }
    
    //算坐标,
//    CGFloat x = cell.frame.origin.x;
//    CGFloat y = cell.frame.origin.y - _detailsCollectionView.contentOffset.y;
//    CGFloat orgWight = cell.frame.size.width;
//    CGFloat orgHeight = cell.frame.size.height;
//    initFrame =CGRectMake(x, y, orgWight, orgHeight);
    //设置图片开始在cell的位置,转换cell的位置信息给动画图片
    imageView.frame = [cell convertRect:cell.bounds toView:containerView];
    initFrame = imageView.frame;
    //获取要缩放的图片
    imageView.image = _image;
    
    //设置图片进行缩放后的高和宽
    CGFloat height = imageView.image.size.height * (fromVC.view.frame.size.width / imageView.image.size.width);
    CGFloat wight = fromVC.view.frame.size.width;
   
    //让图片变大放在屏幕中央
    
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        
        imageView.frame = CGRectMake(0, ((fromVC.view.frame.size.height + NAVHeight) / 2) - (height / 2), wight, height);
        toVC.view.alpha = 1;
    } completion:^(BOOL finished) {
        
        for (UIView *view in toVC.view.subviews)
        {
            view.hidden = NO;
        }
        imageView.hidden = YES;
        //如果动画过渡取消了就标记不完成,否则才完成,这里可以直接写YES,如果有手势过渡才需要判断,必须标记,否则系统不会中动画完成的部署,会出现无法交互之类的bug
        [transitionContext completeTransition:YES];
    }];
}

- (void)doPopAnimateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
   
    //这里是缩小回去的动画,fromVC是大图浏览界面的控制器,toVC是小图浏览界面的控制器。
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    UICollectionView *BigImageCollectionView;
    //找到CollectionView
    for (UIView *view in fromVC.view.subviews)
    {
        if ([view isKindOfClass:[UICollectionView class]])
        {
            BigImageCollectionView = (UICollectionView *)view;
        }
    }
    
    if (!BigImageCollectionView)
    {
        for (UIView *view in fromVC.view.subviews)
        {
            for (UICollectionView *collectionView in view.subviews)
            {
                if ([collectionView isKindOfClass:[UICollectionView class]])
                {
                    BigImageCollectionView = collectionView;
                }
            }
        }
    }
    
    
    //得到大图页面的cell,主要是为了用那张图片,因为动画的图片用的是大图
    UICollectionViewCell *bigImageCell = BigImageCollectionView.visibleCells[0];
    
    //在这里拿到大图界面返回时候的indexPath
    returnIndexPath = [BigImageCollectionView indexPathForCell:bigImageCell];
    
    //这里是之前点击的小图的位置
//    NSIndexPath *initIndexPath = toVC.indexPath;
    
    //这里做一个判断,如果是同一行,那么就不用改变contentSize
    if ((returnIndexPath.item / rowItemCount) !=  (initIndexPath.item / rowItemCount))
    {
        
        //算出原始的cell距离屏幕边缘有几行(用到屏幕边缘的距离对cell的高度取整),如果要返回的cell在下面就用屏幕减去原始cell的最大Y值,否则就用原始的Y值
        NSInteger rowCount = returnIndexPath.item > initIndexPath.item ?  (SCREENHEIGHT - 64 - CGRectGetMaxY(initFrame)) / initFrame.size.height : initFrame.origin.y / initFrame.size.height;
        //得到要返回的cell距离开始的cell距离几个item
        NSInteger abs = returnIndexPath.item - initIndexPath.item;
        //当前的位置距离原始位置几行
        NSInteger distanceCount = labs(abs) / rowItemCount;
        //距离的格子数目小于rowItemCount格(一行就rowItemCount格),那么久让行数加一
        labs(abs) < rowItemCount ? distanceCount++ : distanceCount;
        //当前距离原始的行数大于原始距离屏幕的行数,就需要滚动
        if (distanceCount > rowCount)
        {
            //得到contentSize需要增加或减少的长度
            NSInteger scrollCount = abs > 0 ? distanceCount : -distanceCount;
            //如果
            if(labs(abs) % rowItemCount != 0)
            {
                if (scrollCount > 0)
                {
                    scrollCount += 1;
                }
                else
                {
                    scrollCount -= 1;
                }
                
            }
            smallCollectionView.contentOffset =(CGPoint){0, smallCollectionView.contentOffset.y + (initFrame.size.height + 2) * scrollCount};
        }
    }
    
    //得到要返回的cell,根据indexPath
    UICollectionViewCell *currendCell = [smallCollectionView cellForItemAtIndexPath:returnIndexPath];
    
    
    //这里就是过渡动画的那个视图了
    UIView *containerView = [transitionContext containerView];
    //这里的lastView就是push时候初始化的那个tempView
    UIImageView *tempView = containerView.subviews.lastObject;
    
    tempView.image = _image;
    
    //设置初始状态
    currendCell.hidden = YES;
    fromVC.view.hidden = YES;
    tempView.hidden = NO;
    toVC.view.hidden = NO;
    [containerView insertSubview:toVC.view atIndex:0];
    
    //一个蒙版遮住原来位置的图
    UIView *view;
    if (!currendCell)
    {
        view  = [[UIView alloc] initWithFrame:[self returnFrameWithIndexPath:returnIndexPath AndInitFrame:initFrame]];
        view.backgroundColor = toVC.view.backgroundColor;
        [toVC.view addSubview:view];
    }

    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        
        if (currendCell)
        {
            tempView.frame = [currendCell convertRect:currendCell.bounds toView:containerView];
        }
        else
        {
            tempView.frame = [self returnFrameWithIndexPath:returnIndexPath AndInitFrame:initFrame];
        }
        
        fromVC.view.alpha = 0;
        
    } completion:^(BOOL finished) {
        //如果加入了手势必须判断
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
        if ([transitionContext transitionWasCancelled]) {//手势取消了,原来隐藏的imageView要显示出来
            //失败了隐藏tempView,显示fromVC.imageView
            tempView.hidden = YES;
            fromVC.view.hidden = NO;
        }else{//手势成功,cell的imageView也要显示出来
            //成功了移除tempView,下一次pop的时候又要创建,然后显示cell的imageView
            currendCell.hidden = NO;
            [tempView removeFromSuperview];
            if (!currendCell)
            {
                [view removeFromSuperview];
            }
        }
    }];
    
}

#pragma mark - 计算返回的坐标
- (CGRect)returnFrameWithIndexPath:(NSIndexPath *)indexPath AndInitFrame:(CGRect)initFrame
{
    CGFloat returnWidth = initFrame.size.width;
    CGFloat returnHeight = initFrame.size.height;
    CGFloat returnX = (indexPath.item % rowItemCount) * (initFrame.size.width + 2);
    CGFloat returnY = initFrame.origin.y;
    
    return (CGRect){returnX, returnY, returnWidth, returnHeight};
                                              
}


送上写的一个Demo

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
iOS中的exc_bad_access通常是由于访问了无效的内存地址而触发的错误。要解决这个问题,我们可以遵循以下几个步骤: 1. 检查Crash日志:首先,我们应该查看Crash日志以了解问题的具体原因。Crash日志将显示出错的位置以及相关的堆栈信息,这有助于我们确定问题的根源。 2. 使用断点:如果我们知道大概出错的位置,可以在代码中设置断点来逐步调试。这样,我们可以在错误出现前暂停应用程序的执行,从而更好地分析错误。 3. 检查空指针:空指针访问是常见的exc_bad_access错误。我们应该检查代码中的指针变量是否为空并确保在使用前进行了正确的初始化。 4. 检查内存释放:内存管理是另一个常见的exc_bad_access错误的原因。我们需要确保在释放内存之后不再访问已释放的内存。可以使用工具如Instruments来检测内存泄漏和野指针。 5. 使用ARC(自动引用计数):如果我们的应用程序使用了手动管理内存,那么我们应该考虑迁移到ARC来减少内存管理错误的发生。ARC会自动处理内存释放,从而降低了内存相关的问题。 6. 避免循环引用:循环引用也可能导致exc_bad_access错误。我们应该小心使用强引用和弱引用,以避免循环引用的产生。 7. 更新代码库和依赖项:如果我们使用的是第三方库或依赖项,那么我们应该确保它们是最新的版本并且与我们应用程序的其他部分兼容。有时,exc_bad_access错误可能是由于库或依赖项的错误导致的。 总之,解决exc_bad_access错误需要仔细检查代码和内存管理,并根据具体情况进行调试和修复。通过遵循上述步骤,我们可以更好地理解问题并找到适当的解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值