仿 App Store 首页 Toady 转场效果

APP新功能,依旧应老板要求,要做和App Store首页Today一样的动画想过。
demo地址:仿 App Store 首页 Toady 转场Demo
先放个视频看下效果。

转成GIF太大了,压缩之后又看不清,视频简书又放不了,只能做成连接放进来:
仿App Store Today转场效果视频演示
这个是截图:

像这种转场动画,在code4app能找到很多,但我们老板比较变态(左右看看没人我就放心了),要和App Store一模一样,然后辛苦了三四天,虽然这个还有差了一点,但是已经有差不多有九成像了。

1. 原理:

简单来说就是设置navigationControllerdelegate,重写原生pushpop动画,详情页背景加毛玻璃效果,下拉的时候是TapscorllView手势的结合。
还有不知道你们有没有注意到,首页的Cell点击是有缩小的效果的,所以这里还要修改一下celltransform

2. 实现

a. 画cell

.h
@interface HomeCell : UITableViewCell

@property (strong, nonatomic) UIImageView *bgimageView;
@property (strong, nonatomic) UIView *bgView;
@property (strong, nonatomic) UILabel *titleLabel;
@property (strong, nonatomic) UILabel *contentLabel;
@end

.m
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        
        self.backgroundColor = COLOR_CLEAR;
        self.contentView.backgroundColor = COLOR_CLEAR;
        
        [self.contentView addSubview:self.bgView];
        [self.bgView addSubview:self.bgimageView];
        [self.bgView addSubview:self.titleLabel];
        [self.bgView addSubview:self.contentLabel];
        
    }
    return self;
}

- (UIImageView *)bgimageView {
    if (_bgimageView == nil) {
        _bgimageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH-40, (SCREEN_WIDTH-40)*1.3)];
        _bgimageView.layer.cornerRadius = 15;
        _bgimageView.layer.masksToBounds = YES;
        
    }
    return _bgimageView;
}

- (UIView *)bgView {
    if (_bgView == nil) {
        _bgView = [[UIView alloc]initWithFrame:CGRectMake(20, 0, SCREEN_WIDTH-40, (SCREEN_WIDTH-40)*1.3+25)];
        
        _bgView.backgroundColor = COLOR_CLEAR;
    }
    return _bgView;
}

- (UILabel *)titleLabel {
    if (_titleLabel == nil) {
        _titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(15, 20, SCREEN_WIDTH-30, 30)];
        _titleLabel.textColor = COLOR_WHITE;
        _titleLabel.textAlignment = NSTextAlignmentLeft;
        _titleLabel.font = FONT_B(25);
    }
    return _titleLabel;
}

- (UILabel *)contentLabel {
    if (_contentLabel == nil) {
        _contentLabel = [[UILabel alloc]initWithFrame:CGRectMake(15, (SCREEN_WIDTH-40)*1.3-30, SCREEN_WIDTH-44, 15)];
        _contentLabel.font = FONT_PF(15);
        _contentLabel.alpha = 0.5;
        _contentLabel.textColor = COLOR_WHITE;
        _contentLabel.textAlignment = NSTextAlignmentLeft;
    }
    return _contentLabel;
}

b. tableView


#pragma mark - UITableViewDelegate,UITableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return (SCREEN_WIDTH-40)*1.3+25;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.dataSource.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    HomeCell *cell = [tableView dequeueReusableCellWithIdentifier:@"HOMECELLID"];
    if (cell == nil) {
        cell = [[HomeCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"HOMECELLID"];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
        cell.shouldGroupAccessibilityChildren = YES;
    }
    cell.titleLabel.text = self.titles[indexPath.row];
    cell.contentLabel.text = self.titleTwos[indexPath.row];
    cell.bgimageView.image = [UIImage imageNamed:self.dataSource[indexPath.row]];
    cell.transform = CGAffineTransformMakeScale(1, 1);
    
    return cell;
    
}

重点在这:App Store的Today,手指碰触的时候有个缩小的效果,滑动或放开后回弹,在这实现的
// TODO: 即将进入高亮状态
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath  {
    selectIndexPath = indexPath;
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    [UIView animateWithDuration:0.2 animations:^{
        cell.transform = CGAffineTransformMakeScale(0.9, 0.9);
    }];
    
    return YES;
}
// TODO: 结束高亮状态
- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    NSLog(@"%u",cell.selected);
    
    if ([selectIndexPath isEqual:indexPath]) {
        [UIView animateWithDuration:0.2 animations:^{
            cell.transform = CGAffineTransformMakeScale(1, 1);
            return;
        }];
    }
}

没错,这里少了tableview: didSelectRowAtIndexPath: 别着急,后面说。

c. 画UI,tableViewHeaderView,不贴代码了,都简单。

核心代码来了,重写原生push动画效果。

d. 重写push动画

  • 添加代理
@interface HomeViewController ()<UINavigationControllerDelegate,UIViewControllerAnimatedTransitioning,UITableViewDelegate,UITableViewDataSource>
  • 继承代理
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    // 设置navigaitonControllerDelegate
    self.navigationController.delegate = self;
}
  • 实现代理
#pragma mark - UIViewControllerAnimatedTransitioning
// MARK: 设置代理
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
    return self;
}

// MARK: 设置动画时间
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
    return 1.0f;
}

  • 设置转场动画
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
    
    HomeCell *cell = (HomeCell *)[self.tableView cellForRowAtIndexPath:[self.tableView indexPathForSelectedRow]];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView *toView = [toVC valueForKeyPath:@"QRBGImageView"];
    UIView *fromView = cell.bgView;
    UIView *containerView = [transitionContext containerView];
    UIView *snapShotView = [[UIImageView alloc]initWithImage:cell.bgimageView.image];
    snapShotView.frame = [containerView convertRect:fromView.frame fromView:fromView.superview];
    
    fromView.hidden = YES;
    
    toVC.view.frame = [transitionContext finalFrameForViewController:toVC];
    toVC.view.alpha = 0;
    toView.hidden = YES;
    
    UILabel *titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(15, 20, SCREEN_WIDTH-30, 30)];
    titleLabel.textColor = COLOR_WHITE;
    titleLabel.textAlignment = NSTextAlignmentLeft;
    titleLabel.font = FONT_B(25);
    titleLabel.text = cell.titleLabel.text;
    
    UILabel *contentLabel = [[UILabel alloc]initWithFrame:CGRectMake(15, (SCREEN_WIDTH-40)*1.3-30, SCREEN_WIDTH-44, 15)];
    contentLabel.font = FONT_PF(15);
    contentLabel.textColor = COLOR_WHITE;
    contentLabel.textAlignment = NSTextAlignmentLeft;
    contentLabel.alpha = 0.5;
    contentLabel.text =cell.contentLabel.text;
    [snapShotView addSubview:titleLabel];
    [snapShotView addSubview:contentLabel];
    [containerView addSubview:toVC.view];
    [containerView addSubview:snapShotView];
    
    [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0f usingSpringWithDamping:0.6f initialSpringVelocity:1.0f options:UIViewAnimationOptionCurveLinear animations:^{
        [containerView layoutIfNeeded];
        toVC.view.alpha = 1.0f;
         Tabbar*tabBar = (Tabbar *)self.tabBarController.tabBar;
        if (IPHONE_X) {
            tabBar.frame = CGRectMake(0, SCREEN_HEIGHT, SCREEN_WIDTH, 83);
        } else {
            tabBar.frame = CGRectMake(0, SCREEN_HEIGHT, SCREEN_WIDTH, 49);
        }
        snapShotView.frame = [containerView convertRect:toView.frame fromView:toView.superview];
        titleLabel.frame = CGRectMake(22, 30, SCREEN_WIDTH-30, 30);
        contentLabel.frame = CGRectMake(22, SCREEN_WIDTH*1.3-30, SCREEN_WIDTH*1.3-44, 15);
        
    } completion:^(BOOL finished) {
        
        toView.hidden = NO;
        fromView.hidden = NO;
        [snapShotView removeFromSuperview];
        [self.tableView reloadData];
        [transitionContext completeTransition:!transitionContext.transitionWasCancelled];
    }];
    
}

解释一下:

1、 cell 就不说了,拿 cell 是为了那 fromViewimage
2、转场动画实现的原理就是push页的push内容拍照,然后通过transitionContext传递到下一个页面的指定位置,并带弹跳效果,动画结束隐藏拍照。
3、snapShotView 就是传递的push内容View,好多demo都是用的截图,我这里直接用的 imageView,也是为了仿App StoreToday页面仔细看文字是有移动的,所以这里是直接用图片然后在上面画 titleLabelcontentLabel,之后通过动画修 Label位置。
4、之后就是动画的 block了。

+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion; 
duration:      动画时长
delay:         等待时长
dampingRatio:  表示弹性属性
velocity:      初速度
options:       动画效果
animations:    动画内容操作
completion:    动画结束后调用

5、Tabbar 是自定义的 tabbar,与本节无关,是为了标签栏中间的按钮,Today点击push的时候,标签栏向下移,我找了找,直接修改坐标是最简单的。但是要判断是不是 iphone X,如果是iPhone X的话,高度是 83 。
6、还没结束,不知道有没有人发现,Today页面push的时候,状态栏是上移隐藏掉的。所以我之前在animations:里面添加了隐藏statusBar的方法。

        [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];

然后问题出现了,添加了这个之后,tabbar 的动画效果消失了,并且statusBar的动画效果并没有出现,一开始我以为是动画冲突了,试了好多次,最后在两个 ControllerviewWillAppear 里添加了这样的方法:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    // 设置navigaitonControllerDelegate
    self.navigationController.delegate = self;
    // 隐藏状态栏
    [UIView animateWithDuration:0.2 animations:^{
        [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
    }];
}

之后,tabbarstatusBar的动画效果都有了,具体原因我才疏学浅,不太懂,感觉像是tabbarstatusBar的动画是绑定的,或者他们俩之间同时修改会有冲突。

e. 添加数据了该。

    [self.dataSource addObjectsFromArray:@[@"Home_demo_01",@"Home_demo_02",@"Home_demo_03"]];
    
    [self.titles addObject:@"哈弗H6Coupe震撼上市"];
    [self.titles addObject:@"黑天鹅蛋糕 "];
    [self.titles addObject:@"高端健身会所入驻园区"];
    
    [self.titleTwos addObject:@"体验“中国芯”动力新哈弗H6 Coupe"];
    [self.titleTwos addObject:@"“我的一生,为美而感动,为美而存在”"];
    [self.titleTwos addObject:@"让运动助力工作生活"];
    
    [self.contents addObject:@"前卫设计动感十足 体验“中国芯”动力新哈弗H6 Coupe\n新哈弗H6 Coupe是长城公司采用全新设计语言开发的一款具有酷颜值,酷动力,酷装备的SUV。此次新哈弗H6 Coupe由内而外的全面升级,必将引来大众的追捧,开创哈弗SUV新篇章...\n1.5GDIT发动机应用CVVL、缸内直喷等前沿技术,动力响应快、燃油经济性好,最大功率124kW,1400转爆发最大扭矩285N·m,百公里加速9.7s,百公里油耗仅6.8L,荣获“中国心”2017十佳发动机;\n7DCT湿式双离合变速器采用最先进的摩擦材料,速比范围高达8.0,换挡平顺、可靠性高,提升燃油经济性,荣获2018届世界十佳变速器。\n整车采用超刚性一体式安全结构车身;\n配备奥托立夫6安全气囊、帘式气囊贯穿前后,保护面积更宽泛;\n盲点侦测,保证行车安全;\n配备360环视影像系统、LED组合前灯,驾乘更安全。\n外观采用钻石形体光学设计,车身线条硬朗连贯,更动感;\n内饰采用大面贯穿式整体设计,断面式仪表板极具立体感,打造科技及尊享兼备的体验。"];
    [self.contents addObject:@"一兆韦德健身管理有限公司目前有员工3000多人,拥有超过一百家健身会所。公司大力倡导绿色环保和时尚健身运动。凭借先进的管理理念、丰富的行业经验、完善的管理体系和管理团队,业已成为业内发展速度快、分店数量多、会员数量众多的健身连锁机构。公司多次通过权威机构认证,目前由国际著名投资公司——新加坡淡马锡集团注资,2015年更是获得了上海市著名商标,是健身行业内首个获得该荣誉的企业。公司希望通过全体员工的努力为社会提供有价值的健康生活服务,成为具世界竞争力的连锁健身企业之一。公司将打造更多的绿色生态会馆、为美好的城市生活贡献更多力量。"];
    [self.contents addObject:@"黑天鹅 \n隶属于北京黑天鹅餐饮管理有限公司,公司主要打造国内品质卓越,美味安心的蛋糕。黑天鹅蛋糕源于黄金比例的配方,精选世界各地优质食材,让您和朋友轻松享受精品蛋糕。\n用新锐的艺术理念和国际化的视野,带领团队重塑品牌,开启了黑天鹅与全球顶尖的甜品大师、设计大师和顶级原料商全面合作的阶段,让黑天鹅的产品和形象获得蜕变和飞跃,迅速跻身于国际一流烘焙品牌的行列。\n黑天鹅蛋糕推出以来,一直以一种“昂贵、奢华、精美”的形象示人,北京的首家实体店铺,自然要延续这个风格。先站在门口拍一张,这种风格的铺面,在蛋糕店里绝对令人耳目一新。"];

都是捡的假数据,图片,主标题,副标题,内容。

f. 该didSelectRowAtIndexPath

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
    HomeCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    cell.transform = CGAffineTransformMakeScale(0.9, 0.9);
    HomeDetailViewController *detail = [[HomeDetailViewController alloc]init];
    detail.selectIndexPath = indexPath;
    // 截屏
    detail.bgImage = [self imageFromView];
    detail.titles = self.titles[indexPath.row];
    detail.titleTwo = self.titleTwos[indexPath.row];
    detail.content = self.contents[indexPath.row];
    detail.imageName = self.dataSource[indexPath.row];
    [self.navigationController pushViewController:detail animated:YES];
}

截屏代码

#pragma mark - 截屏

- (UIImage *)imageFromView {
    UIGraphicsBeginImageContext(self.view.frame.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [self.view.layer renderInContext:context];
    UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return theImage;
}

截屏是为了 detail 页面里,下拉时候显示的毛玻璃背景,毛玻璃效果在detail页面里。然后就是假数据都传过去。

到这里 push 页面的主要功能代码就都在这了,下面是 detail 页面的代码实现。

 

3. detail页面代码实现

 
detail 页面我也是用 tableView 来搭建的,图片作为tableViewHeaderView,内容放在 cell 里,为的是以后方便实现图文混排的模式,一个 cell 仿文字,一个 cell 放图片。

** UI就不说了,就是tableView的基本用法,cell里放文字,计算文字高度,直接说重点***

a. 毛玻璃

- (void)buildSubviews {
    [self.view addSubview:self.bgImageView];
    self.bgImageView.image = self.bgImage;
    //毛玻璃
    UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
    UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:effect];
    effectView.frame = CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
    [self.view addSubview:effectView];
    
    [self.view addSubview:self.tableView];
    self.tableView.tableHeaderView = [self tableViewHeaderView];
}

b. tableViewHeaderView 添加 pan 手势

- (UIView *)tableViewHeaderView {
    UIView *headerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_WIDTH*1.3)];
    
    self.QRBGImageView.frame = CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_WIDTH*1.3);
    self.QRBGImageView.image = [UIImage imageNamed:self.imageName];
    [headerView addSubview:self.QRBGImageView];
    // 添加手势
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
    [self.QRBGImageView addGestureRecognizer:pan];
    pan.delegate = self;
    
    self.titleLabel.text =self.titles;
    self.titleTwoLabel.text = self.titleTwo;
    [headerView addSubview:self.titleLabel];
    [headerView addSubview:self.titleTwoLabel];
    return headerView;
}

c. pan 手势Click


#pragma mark - 下拉缩小,跳转

- (void)pan:(UIPanGestureRecognizer *)pan {
    switch (pan.state) {
        case UIGestureRecognizerStateBegan: {   // 手势开始
            CGPoint currentPoint =[pan locationInView:self.QRBGImageView];
            startPointY = currentPoint.y;
            //NSLog(@"手势开始 (%f,%f)",currentPoint.x,currentPoint.y);
        } break;
        case UIGestureRecognizerStateChanged: { // 手势状态改变
            CGPoint currentPoint =[pan locationInView:self.QRBGImageView];
            
            scale = (SCREEN_HEIGHT-(currentPoint.y-startPointY))/SCREEN_HEIGHT;
            if (scale > 1.0f) {
                scale = 1.0f;
            } else if (scale <=0.8f) {
                scale = 0.8f;
                
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.03 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    [self.navigationController popViewControllerAnimated:YES];
                });
                
            }
            if (self.tableView.contentOffset.y<=0) {
                // 缩放
                self.tableView.transform = CGAffineTransformMakeScale(scale, scale);
                // 圆角
                self.tableView.layer.cornerRadius = 15 * (1-scale)*5*1.08;
            }
            
            if (scale < 0.99) {
                [self.tableView setScrollEnabled:NO];
            } else {
                [self.tableView setScrollEnabled:YES];
            }
        } break;
        case UIGestureRecognizerStateEnded:  { // 手势结束
            NSLog(@"手势结束");
            scale = 1;
            self.tableView.scrollEnabled = YES;
            if (scale>0.8) {
                [UIView animateWithDuration:0.2 animations:^{
                    self.tableView.layer.cornerRadius = 0;
                    self.tableView.transform = CGAffineTransformMakeScale(1, 1);
                }];
            }
            
        }  break;
        default:
            break;
    }
}
  1. 设置tableView的最小缩放大小是0.8,小于0.8就执行pop。
  2. 结束手势的时候回到初始大小。

d.UIGestureRecognizerDelegate 为了解决手势冲突问题

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    if ([otherGestureRecognizer.view isKindOfClass:[UITableView class]]) {
        return YES;
    }
    return NO;
}

e. tableView 的滑动监控,根据Today的动画效果,详情页面向下惯性滑动的时候,图片和文字是停在最上面不动。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (scrollView.contentOffset.y <= 0) {
        
        CGRect rectQ = self.QRBGImageView.frame;
        rectQ.origin.y = scrollView.contentOffset.y;
        self.QRBGImageView.frame = rectQ;
        
        CGRect rectT = _titleLabel.frame;
        rectT.origin.y = scrollView.contentOffset.y+30;
        _titleLabel.frame = rectT;
        
        CGRect rectC = _titleTwoLabel.frame;
        rectC.origin.y = scrollView.contentOffset.y +SCREEN_WIDTH*1.3-30;
        _titleTwoLabel.frame = rectC;
    }
}

重点来了:重写pop动画,和push基本一样,直接贴代码了

#pragma mark - UIViewControllerAnimatedTransitioning

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
    return self;
}

- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
    return 1.0f;
}
#pragma mark - 重写pop动画

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
    
    HomeViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    HomeDetailViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIView *containerView = [transitionContext containerView];
    UIView *fromView = [fromVC valueForKeyPath:@"QRBGImageView"];
    toVC.view.frame = [transitionContext finalFrameForViewController:toVC];
    HomeCell *cell = (HomeCell *)[toVC.tableView cellForRowAtIndexPath:self.selectIndexPath];
    UIView *originView = cell.bgimageView;
    
    UIView *snapShotView = [fromView snapshotViewAfterScreenUpdates:NO];
    snapShotView.layer.masksToBounds = YES;
    snapShotView.layer.cornerRadius = 15;
    snapShotView.frame = [containerView convertRect:fromView.frame fromView:fromView.superview];
    
    fromView.hidden = YES;
    originView.hidden = YES;
    
    [containerView insertSubview:toVC.view belowSubview:fromVC.view];
    [containerView addSubview:snapShotView];
    UILabel *titleLabel = [[UILabel alloc]init];
    titleLabel.textColor = COLOR_WHITE;
    titleLabel.textAlignment = NSTextAlignmentLeft;
    titleLabel.font = FONT_B(25);
    titleLabel.text = self.titles;
    
    UILabel *contentLabel = [[UILabel alloc]init];
    contentLabel.font = FONT_PF(15);
    contentLabel.textColor = COLOR_WHITE;
    contentLabel.textAlignment = NSTextAlignmentLeft;
    contentLabel.text =self.titleTwo;
    contentLabel.alpha = 0.5;
    titleLabel.frame = CGRectMake(22, 20, SCREEN_WIDTH-30, 30);
    contentLabel.frame = CGRectMake(22, SCREEN_WIDTH*1.3-30, SCREEN_WIDTH*1.3-44, 15);
    [snapShotView addSubview:titleLabel];
    [snapShotView addSubview:contentLabel];

    [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:0.5f initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
        [containerView layoutIfNeeded];
        fromVC.view.alpha = 0.0f;
        snapShotView.layer.cornerRadius = 15;
        self.tableView.frame = CGRectMake(self.tableView.frame.origin.x, self.tableView.frame.origin.y, self.tableView.frame.size.width,SCREEN_WIDTH*1.3*0.8);
        self.tableView.layer.cornerRadius = 15;
        snapShotView.frame = [containerView convertRect:originView.frame fromView:originView.superview];
        titleLabel.frame = CGRectMake(15, 20, SCREEN_WIDTH-30, 30);
        contentLabel.frame = CGRectMake(15, (SCREEN_WIDTH-40)*1.3-30, SCREEN_WIDTH-44, 15);
        Tabbar *tabBar = (Tabbar *)self.tabBarController.tabBar;
        if (IPHONE_X) {
            tabBar.frame = CGRectMake(0, SCREEN_HEIGHT-83, SCREEN_WIDTH, 83);
        } else {
            tabBar.frame = CGRectMake(0, SCREEN_HEIGHT-49, SCREEN_WIDTH, 49);
        }

        
    } completion:^(BOOL finished) {
        fromView.hidden = YES;
        [snapShotView removeFromSuperview];
        originView.hidden = NO;
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];
}

差不多就这么多了,写的比较乱,有需要可以直接下demo,demo地址放在最上面了。
没有进行封装,是因为我们的APP里面是有调取本地相机和相册的操作,封装之后与系统动画冲突,崩的一塌糊涂。如果只是简单的几个页面用到这个效果的话,不建议封装。

更新:2018-03-24 11:00

之前的做法,当手指放在文字部分,滑动到contentOffSet.y = 0时候,再向下滑动页面没有缩放功能,然后我刚才灵光一闪,把pan手势加到tableView上

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
    [self.tableView addGestureRecognizer:pan];
    pan.delegate = self;

- (void)pan:(UIPanGestureRecognizer *)pan方法中
locationInView:加到tableView上,

- (void)pan:(UIPanGestureRecognizer *)pan {
    switch (pan.state) {
        case UIGestureRecognizerStateBegan: {   // 手势开始
            CGPoint currentPoint =[pan locationInView:self.tableView];
            startPointY = currentPoint.y;
            //NSLog(@"手势开始 (%f,%f)",currentPoint.x,currentPoint.y);
        } break;
        case UIGestureRecognizerStateChanged: { // 手势状态改变
            CGPoint currentPoint =[pan locationInView:self.tableView];
            

OK,没有冲突,和App Store的动画一样的效果,
目前还差右划返回

更新:2018-03-25 14:50

添加了横划返回

添加了两个属性

    CGFloat startPointX;
    BOOL isHorizontal;

- (void)pan:(UIPanGestureRecognizer *)pan中,判断手指位置,判断是否可以横划,如果可以横划,判断滑动方向。

- (void)pan:(UIPanGestureRecognizer *)pan {
    switch (pan.state) {
        case UIGestureRecognizerStateBegan: {   // 手势开始
            CGPoint currentPoint =[pan locationInView:self.tableView];
            startPointY = currentPoint.y;
            startPointX = currentPoint.x;
            // 判断是否可以横划,当手指在左侧的时候,可以横划
            if (startPointX>30) {
                isHorizontal = NO;
            } else {
                isHorizontal = YES;
            }
        } break;
        case UIGestureRecognizerStateChanged: { // 手势状态改变
            CGPoint currentPoint =[pan locationInView:self.tableView];
            
            // 如果可以横划,判断是横划还是竖划,如果是横划,那么滑动的距离一定是大于竖划的
            if (isHorizontal) {
                if ((currentPoint.x-startPointX)>(currentPoint.y-startPointY)) {
                    scale = (SCREEN_WIDTH-(currentPoint.x-startPointX))/SCREEN_WIDTH;
                } else {
                    scale = (SCREEN_HEIGHT-(currentPoint.y-startPointY))/SCREEN_HEIGHT;
                }
            } else {
                scale = (SCREEN_HEIGHT-(currentPoint.y-startPointY))/SCREEN_HEIGHT;
            }
            
            if (scale > 1.0f) {
                scale = 1.0f;
            } else if (scale <=0.8f) {
                scale = 0.8f;
                
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.03 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    [self.navigationController popViewControllerAnimated:YES];
                });
                
            }
            // 缩放
            self.tableView.transform = CGAffineTransformMakeScale(scale, scale);
            // 圆角
            self.tableView.layer.cornerRadius = 15 * (1-scale)*5*1.08;
            
            if (scale < 0.99) {
                [self.tableView setScrollEnabled:NO];
            } else {
                [self.tableView setScrollEnabled:YES];
            }
        } break;
        case UIGestureRecognizerStateEnded:  { // 手势结束
            NSLog(@"手势结束");
            scale = 1;
            self.tableView.scrollEnabled = YES;
            if (scale>0.8) {
                [UIView animateWithDuration:0.2 animations:^{
                    self.tableView.layer.cornerRadius = 0;
                    self.tableView.transform = CGAffineTransformMakeScale(1, 1);
                }];
            }
            
        }  break;
        default:
            break;
    }
}

GitHub中代码已更新。仿 App Store 首页 Toady 转场Demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335