动画四

后面的东西,可能就比较繁琐,因为像前面的做法可以说是比较固定的,算是比较好的一个方法(当然也有别的方法),但是后面的做法,就是要试着来了。
我起初是想把collectionViewCell直接拉大,变成屏幕这么大的,可是看着后面界面的操作一定是需要一个控制器权限的,所以就思考用导航控制器来衔接,一方面现在的app也多是基于导航的,便于整合,另一方面也算对上面疑问的一个解决。。。然后顺着很费事的做完了,回头想想,这样做不见得好。因为赶时间做别的事,只能这样了。

基于上面的思考,可以说会用到专场动画这套东西,但是我们还是从简单的入手,先用push将就着,在push过去的控制器里先布置好我们控件的位置。

AnotherController

我初步预想的是转场用UIView的动画来做,所以为了便于获取几个大控件位置,又不会被屏幕大小影响的情况下,直接用frame去写这些大的控件。然后为了起初省劲,又直接用上面view,下面tableView的方式,其实现在觉得用个headerView也不错

    //topView
    self.topImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[CardModel cardModel][self.index].cardPicName]];
    self.topImageView.userInteractionEnabled = YES;
    self.topImageView.frame = CGRectMake(0, -1.5 * Marign, ScreenWidth, TopViewHeight + 1.5 * Marign);
    self.topImageView.contentMode = UIViewContentModeScaleToFill;
    [self.view addSubview:self.topImageView];
    
    self.titleView = [[UIView alloc] initWithFrame:CGRectMake(0, TopViewHeight , ScreenWidth, TitleViewHeight)];
    self.titleView.backgroundColor = [UIColor whiteColor];
    [self.view addSubview:self.titleView];

    //tabView
    self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, TitleViewHeight + TopViewHeight, ScreenWidth, ScreenHeight - (TitleViewHeight + TopViewHeight))];
    [self.tableView registerClass:[CardTableViewCell class] forCellReuseIdentifier:CellId];
    self.tableView.delegate = self;
    self.tableView.dataSource = self;
    
    self.tableView.rowHeight = UITableViewAutomaticDimension;
    self.tableView.estimatedRowHeight = 60;
    
    self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLineEtched;
    
    self.tableView.bounces = NO;
    self.tableView.showsVerticalScrollIndicator = NO;
    
    [self.view addSubview:self.tableView];

对小控件还是用masonry,可以整体移动。

    //标题
    UILabel *titleLabel = [UILabel cz_labelWithText:@"xxxxx * 100" fontSize:16 color:[UIColor lightGrayColor]];
    titleLabel.numberOfLines = 1;
    [self.titleView addSubview:titleLabel];
    [titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.titleView.mas_top).offset(15);
        make.left.equalTo(self.view.mas_left).offset(15);
        make.centerX.equalTo(self.view.mas_centerX);
    }];
    
    //评价
    UILabel *comment = [UILabel cz_labelWithText:@"100 comments" fontSize:14 color:[UIColor grayColor]];
    comment.textAlignment = NSTextAlignmentLeft;
    [self.titleView addSubview:comment];
    [comment mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(titleLabel.mas_bottom).offset(10);
        make.left.equalTo(self.titleView.mas_left).offset(15);
        make.height.mas_equalTo(@20);
    }];
    
    //星星
    CardStartsView *startView = [[CardStartsView alloc] init];
    startView.level = 4;
    [self.titleView addSubview:startView];
    [startView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerY.equalTo(comment.mas_centerY);
        make.left.equalTo(comment.mas_right).offset(10);
        make.width.mas_equalTo(@70);
        make.height.mas_equalTo(@20);
    }];

    //返回按钮
    UIButton *backBtn = [[UIButton alloc] init];
    [backBtn setImage:[UIImage imageNamed:@"back"] forState:UIControlStateNormal];
    [backBtn addTarget:self action:@selector(clickBackBtn) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:backBtn];
    [backBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(27);
        make.left.equalTo(self.view.mas_left).offset(15);
        make.width.height.mas_equalTo(@30);
    }];

然后先随便写一写数据源方法,做个cell什么的,之后开始写转场

转场动画

转场依赖于UIViewControllerAnimatedTransitioning
我们做个继承NSObject的类,去实现UIViewControllerAnimatedTransitioning里面的要求就好了。
调两个方法,一个设置动画时长,另一个设置如何执行转场动画

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

//如何执行转场动画
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    switch (_type) {
        case CardTransitionPush:
            [self pushAnimation:transitionContext];
            break;
        
        case CardTransitionPop:
            [self popAnimation:transitionContext];
            break;
    }
}

这上面的pushAnimation和popAnimation就是我们自己要写的了,这两个方法看起来是差不多的,实际上也是,但是因为很多小原因,不方便写在一起,就分开来写了
代码的内容通过中转的导航获取到它上一个控制器和下一个控制器,把上一个的位置通过UIView动画改成下一个位置,因为长时间没写,所以实现起来比较费时。主要很多方法不记得了。里面的层次还是比较重要的,如果写乱了,就会出现很丑的东西,在pop时候也很麻烦

//执行push动画
- (void)pushAnimation:(id<UIViewControllerContextTransitioning>)transitionContext
{
    //起始页面
    ViewController *fromVC = (ViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    //目标页面
    AnotherViewController *toVC = (AnotherViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    //拿到当前点击的cell的imageView
    CardCollectionViewCell *cell;
    NSArray<CardCollectionViewCell *> *cellArray = [fromVC.cardScrollView.collectionView visibleCells];
    for (int i = 0; i < cellArray.count; i++) {
        if (fromVC.currentIndex == cellArray[i].index) {
            cell = (CardCollectionViewCell *)cellArray[i];
        }
    }
    
    UIView *containerView = [transitionContext containerView];
    
    //图片
    UIView *imageView = [cell.coverImageView snapshotViewAfterScreenUpdates:NO];
    imageView.frame = [cell.coverImageView convertRect:cell.coverImageView.bounds toView: containerView];
    
    //titleView
    UIView *titleView = [cell.titleView snapshotViewAfterScreenUpdates:NO];
    titleView.frame = [cell.titleView convertRect:cell.titleView.bounds toView:containerView];
    
    //bgView
    UIView *bgView = [cell.bgView snapshotViewAfterScreenUpdates:NO];
    bgView.frame = [cell.bgView convertRect:cell.bgView.bounds toView:containerView];
    
    //设置动画前的各个控件的状态
    cell.coverImageView.hidden = YES;
    cell.titleView.hidden = YES;
    cell.bgView.hidden = YES;
    toVC.view.alpha = 0;
    toVC.topImageView.hidden = YES;
    toVC.titleView.hidden = YES;
    toVC.tableView.hidden = YES;
    
    //注意添加的层次关系
    [containerView addSubview:toVC.view];
    [containerView addSubview:bgView];
    [containerView addSubview:titleView];
    [containerView addSubview:imageView];
    
    //开始做动画
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        
        //titleView frame
        CGRect titleFrame = titleView.frame;
        titleFrame.origin = [toVC.titleView convertPoint:toVC.titleView.bounds.origin toView:containerView];
        titleView.frame = titleFrame;
        toVC.view.alpha = 1;
        //bgView
        bgView.frame = CGRectMake(0, 300, ScreenWidth, ScreenHeight - 300);
        //图片frame
        imageView.frame = [toVC.topImageView convertRect:toVC.topImageView.bounds toView:containerView];
    } completion:^(BOOL finished) {
        imageView.hidden = YES;
        toVC.topImageView.hidden = NO;
        titleView.hidden = YES;
        bgView.hidden = YES;
        toVC.titleView.hidden = NO;
        toVC.tableView.hidden = NO;
        [transitionContext completeTransition:YES];
    }];
}

在pop里面我们拿出存在[transitionContext containerView]里面的控件,然后让它们该显示的显示,该隐藏的隐藏

//执行pop动画
- (void)popAnimation:(id<UIViewControllerContextTransitioning>)transitionContext
{
    //起始页面
    AnotherViewController *fromVC = (AnotherViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    //目标页面
    ViewController *toVC = (ViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    //拿到当前点击的cell的imageView
    CardCollectionViewCell *cell;
    NSArray<CardCollectionViewCell *> *cellArray = [toVC.cardScrollView.collectionView visibleCells];
    for (int i = 0; i < cellArray.count; i++) {
        if (toVC.currentIndex == cellArray[i].index) {
            cell = (CardCollectionViewCell *)cellArray[i];
        }
    }
    
    UIView *containerView = [transitionContext containerView];
    
    //这个根据之前push时添加的顺序确定的
    //topImageView
    UIView *imageView = containerView.subviews.lastObject;
    //titleView
    UIView *titleView = containerView.subviews[2];
    //bgView
    UIView *bgView = containerView.subviews[1];
    
    //设置初始时的状态
    cell.coverImageView.hidden = YES;
    cell.titleView.hidden = YES;
    fromVC.topImageView.hidden = YES;
    fromVC.titleView.hidden = YES;
    imageView.hidden = NO;
    titleView.hidden = NO;
    bgView.hidden = NO;
    [containerView insertSubview:toVC.view atIndex:0];
    
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        imageView.frame = [cell.coverImageView convertRect:cell.coverImageView.bounds toView:containerView];
        titleView.frame = [cell.titleView convertRect:cell.titleView.bounds toView:containerView];
        bgView.frame = [cell.bgView convertRect:cell.bgView.bounds toView:containerView];
        fromVC.view.alpha = 0;
    } completion:^(BOOL finished) {
        cell.coverImageView.hidden = NO;
        cell.titleView.hidden = NO;
        cell.bgView.hidden = NO;
        [imageView removeFromSuperview];
        [titleView removeFromSuperview];
        [bgView removeFromSuperview];
        [transitionContext completeTransition:YES];
    }];
}

上面要说的就是我们在之前collectionViewCell里面是有个bgView做底板的,但是在下一个控制器,这个view就不用了,但是我们也是要过渡的,不然的话就会很丑。
还有就是用户视图,那个也是要隐藏的。

这样我们就可以回到下一个控制器里,去指定一下转场动画了

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
    return [CardTransition transitionWithType:operation == UINavigationControllerOperationPush ?
                          CardTransitionPush : CardTransitionPop];
}

这时候就会出现一个问题,就是那张图片,图片本来是竖为高的,但是转过来的话,我们不可能给它预留过多的高度,因为要给tableView留有空间,但是又不能让他自适应,因为你的转场动画要自然,就好像真的是放大过去的,转过去,一消失再把图片刷地自适应一下,会很丑,所以这确实是一个由之前决策引起的问题。所以要去思考解决思路,我想到的就是把图片的高度扩展到屏幕外,这样行的通,但是图片的内容也很大程度上可能会出去。但是图片高度出去的部分小,还是会被压缩的很丑。所以我们想着上下各扩大去一部分,留有中间关键位置,但是还是有问题,在转场的时候图片会盖住titleView,直到结束以后,才会显现出来。我思前想后,又想了一个方法,就是通过- (void)viewDidAppear:(BOOL)animated在显示结束以后,再使它发生一次快速位移,把下部分遮掉,效果也还可以,算是告一段落这个头疼的问题

    [super viewDidAppear:animated];
    [UIView animateWithDuration:0.3 animations:^{
        self.titleView.frame = CGRectMake(0, TopViewHeight - Marign, ScreenWidth, Marign);
        self.titleView.alpha = 0.9;
        self.tableView.frame = CGRectMake(0, TitleViewHeight + TopViewHeight - Marign - 30, ScreenWidth, ScreenHeight - (TitleViewHeight + TopViewHeight - Marign - 30));
    }];

你们肯定发现了,我有个两个效果图在转场里的东西避而不谈,一个就是那个tableView逐渐显现,另外一个就是用户头像的动画移动。因为这两个我是稍微试了一下,在现有的情况下,都不大好做,所以又放了一下,还是从简单的着手,我们去处理cell,这个也是必要的,因为它间接关系着头像动画移动.

TableViewCell

关于cell,昵称,头像都是好解决的,相对来说,评论比较麻烦,因为高度由模型控制,我们做点数据进去试试,然后先用UITableViewAutomaticDimension自动计算行高,
需要注意的就是那个评论就不用指定高度了,只需要指定它的底部和contentView的约束关系就好

    //用户评论
    self.userComment = [UILabel cz_labelWithText:self.model.userComment fontSize:14 color:[UIColor darkGrayColor]];
    self.userComment.numberOfLines = 0;
    self.userComment.textAlignment = NSTextAlignmentLeft;
    [self.contentView addSubview:self.userComment];
    [self.userComment mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.userImage.mas_bottom).offset(10);
        make.left.equalTo(self.contentView.mas_left).offset(15);
        make.right.equalTo(self.contentView.mas_right).offset(-15);
        make.bottom.equalTo(self.contentView).offset(-15);
    }];

我们来看下效果图,在过渡的时候还是有些僵硬,是因为上面提到的两部分的缺失。我们后面会说这个。


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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,509评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,024评论 4 62
  • 昨日,北京时间2018年2月7日,电视剧《恋爱先生》收官,相信大家都会和我有相同的感受——没看够,没过瘾。 首先,...
    9b3632873333阅读 650评论 0 51
  • 铁,麻布,小麦的价值,虽然看不见,但却存在于诸物自体内。必须与金相等,于金发生关系(只在它脑中存在的关系),它们的...
    爱吃面条不爱喝汤阅读 174评论 0 1
  • 我是04年到深圳的,跟敏的相识再简单不过,她是公司经理的女朋友,公司组织活动时,能带家属,经理的家属自然能来。她...
    小丫屠阅读 309评论 4 0