开始思考上面留下的两个问题。
TableView的过渡动画思路
先从taleView的过渡开始,这个我是做了很多的测试,如果我们把bgView继承改成TableView的话,在放大的过程中,cell的高度不会变,就会横向拉长,会很丑,然后再刷地一下恢复正常。后来我在约束的设置中引入了Scala参数,就是根据bgView 和 屏幕宽的 比例,整体把约束乘上这个参数,但是效果还是不尽如人意,从我的感觉来看还是不好看。 就是这么个东西,感兴趣可以试一下,也许再微调一下效果就比较好。
((ScreenWidth * (3.0 / 5)) / ScreenWidth)
后来我思考我们既然可以拿到下一个控制器,那就是说可以把下个控制器里的tableview按一定的高度截成图,然后在转场里面传递给bgView,这样就是可以很完美的匹配,但是在开始之初,因为collectionViewCell的宽高比的原因,就拉长的不大好,但是我们可以把这个cell的宽高调整一下,这样效果应该就会好。
总体来说,这两个思路都是可行的,但是又懒得大改了,所以我再尝试后,就先放着,后来做完头像移动后,觉得好像不用这个也还行,就没做,可以自行去尝试一下。
所以接下去,我们继续讲头像移动
CAKeyframeAnimation关键帧动画
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage circleImage:[NSString stringWithFormat:@"%ld",(i + 1) * 10]]];
imageView.frame = CGRectMake(0, 0, 30, 30);
imageView.center = point;
UIWindow *keyW = [UIApplication sharedApplication].keyWindow;
[keyW addSubview:imageView];
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:point];
CGPoint endP = CGPointFromString(_arrM[i]);
[path addLineToPoint:endP];
anim.path = path.CGPath;
anim.duration = AnimationDuration;
anim.delegate = self;
imageView.center = endP;
[anim setValue:imageView forKey:@"LL"];
[imageView.layer addAnimation:anim forKey:nil];
这个动画在我们应用起来就有几个难点,因为我们不用考虑曲线问题,也是那个贝塞尔点是不用计算指定的,只要起点和终点,但是就起点和终点而言,都由于嵌套深的缘故,他们的位置都不好获取,所以就要想点办法
UIWindow *keyW = [UIApplication sharedApplication].keyWindow;
- (CGRect)convertRect:(CGRect)rect toView:(nullable UIView *)view;
通过这两个方法,来计算某个控件在父控件中的位置相对于大屏幕的位置是比较常见的,但是它们有一个缺陷,就是只能用来计算嵌套相对较少的情况,深的话会不准,我呢,就比较懒,我就用这套方法,反复试验了几次,找到了在collectionViewCell里面的用户视图的位置,但是这个位置x是可以直接的获取,y的话获取以后要加一个cell的高度然后还有一个 -12的偏移,要是屏幕适配的话,应该去 -12可能会有不同,测试一下,加个版本判断也是可以的,因为这是个耗时的操作,所以我就直接用了这个结果,90 + i * 25, 141 + (ScreenHeight * (5.0 / 9)),但是我是推荐不要用这种魔法数字,当然我是获取到的,因为比较懒就直接用结果了,哈哈。
然后后面我们要解决的是终点的位置,我们起初用的是UITableViewAutomaticDimension这个东西在自动计算行高的,但是这样算完他不会给行高具体是多少,所以我们就要修改一下,用那种自己算的方法来,自己算就是算个评论的高度,用这个就行
CGSize size = [model.userComment boundingRectWithSize:CGSizeMake(ScreenWidth - 15, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:14]} context:nil].size;
然后我们在模型的set方法里面进行更新初始化的约束,但是不要直接把UI初始化放在里面,会有复用的问题,仅仅根据size.height更新一下高度约束就好
[self.userComment mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(@(size.height));
}];
但是因为我们用的是约束这种东西,他们比frame运算靠后,所以比较麻烦,还好有个layoutIfNeeded,我们在更新完约束以后,调用一下这个方法,让他直接算出来位置,不然的话,当时是没办法获取位置的
[self layoutIfNeeded];
之后我们就可以用个属性把它的高度获取到传递到外面
- (CGFloat)cellHeight
{
return CGRectGetMaxY(self.userComment.frame) + 15;
}
然后我们在控制器的返回行高的方法里,获取到行高,计算头像位置,存到一个数组里面,这样我们就可以在转场动画里面把这个数组获取到
- (NSMutableArray *)calculatePicCenter
{
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:4];
CGFloat start = TitleViewHeight + TopViewHeight;
CGFloat centerX = 30;
for (NSInteger i = 0; i < 4; i++) {
CGFloat centerY = start + 10 + 15;
[arr addObject:NSStringFromCGPoint(CGPointMake(centerX, centerY))];
start += [self.arrCellHeight[i] floatValue];
}
return arr;
}
然后就可以顺利的去实现我们的动画,执行完成以后把图片移除就好了。
- (void)animation:(NSInteger)count
{
//90 + i * 25, 141 + (ScreenHeight * (5.0 / 9)) ?
for (NSInteger i = 0; i < count; i++) {
CGPoint point = CGPointMake(90 + i * 25, 141 + (ScreenHeight * (5.0 / 9)));
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage circleImage:[NSString stringWithFormat:@"%ld",(i + 1) * 10]]];
imageView.frame = CGRectMake(0, 0, 30, 30);
imageView.center = point;
UIWindow *keyW = [UIApplication sharedApplication].keyWindow;
[keyW addSubview:imageView];
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:point];
CGPoint endP = CGPointFromString(_arrM[i]);
[path addLineToPoint:endP];
anim.path = path.CGPath;
anim.duration = AnimationDuration;
anim.delegate = self;
imageView.center = endP;
[anim setValue:imageView forKey:@"LL"];
[imageView.layer addAnimation:anim forKey:nil];
}
}
//动画停止的代理
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
//从anim中取出图片,移除
UIImageView *imgV = [anim valueForKey:@"LL"];
[imgV removeFromSuperview];
}
还有一个细节性的问题,就是这样的话,我们转场的时候原有的用户视图还是会存在的,这是因为我们之前约束,层次的关系,即便我们让它隐藏,也得是再执行完动画才能隐藏,所以要做的就是在对应的位置更改用户头像的约束,让它直接依附于shadowView,然后就可以在这里直接隐藏了,这样我们的动画就算基本完成了。
效果是这样的:
现在还存在几个问题,就是留给别人去处理的,我要去做正事了,哈哈,当然都是小问题
1.:我的模拟器iphone7上的转场会有问题,我因为很长时间不动这套了,不知道他们有什么变化没,所以可以看看
2.:上面谈到的头像移动的起始位置的魔法值的问题
3.: 那个TableView过渡动画添加的问题
4.:把数据甩出去,正正经经做个对接口
5.:代码没有实体文件夹,很乱
我的代码放在链接: https://pan.baidu.com/s/1VruBLLZY9htHhI5wIMpfPg 密码: sywf ,是里面是对应这个目录的五份代码,其实前面的3份自己去实现都是比较容易的。
另外,就是在后面可能(- -。等我有空吧),会更新一些别的,比如一些现在app里面好玩的效果功能,还有大型框架的逻辑,仿写这些。。
网友评论