仿 App Store 首页 Toady 转场效果
APP新功能,依旧应老板要求,要做和App Store首页Today一样的动画想过。
demo地址:仿 App Store 首页 Toady 转场Demo
先放个视频看下效果。
转成GIF太大了,压缩之后又看不清,视频简书又放不了,只能做成连接放进来:
仿App Store Today转场效果视频演示
这个是截图:
像这种转场动画,在code4app能找到很多,但我们老板比较变态(左右看看没人我就放心了),要和App Store一模一样,然后辛苦了三四天,虽然这个还有差了一点,但是已经有差不多有九成像了。
1. 原理:
简单来说就是设置navigationController
的delegate
,重写原生push
和pop
动画,详情页背景加毛玻璃效果,下拉的时候是Tap
和scorllView
手势的结合。
还有不知道你们有没有注意到,首页的Cell
点击是有缩小的效果的,所以这里还要修改一下cell
的transform
。
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
是为了那 fromView
和 image
。
2、转场动画实现的原理就是push
页的push
内容拍照,然后通过transitionContext
传递到下一个页面的指定位置,并带弹跳效果,动画结束隐藏拍照。
3、snapShotView
就是传递的push
内容View
,好多demo
都是用的截图,我这里直接用的 imageView
,也是为了仿App Store,Today页面仔细看文字是有移动的,所以这里是直接用图片然后在上面画 titleLabel
和 contentLabel
,之后通过动画修 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
的动画效果并没有出现,一开始我以为是动画冲突了,试了好多次,最后在两个 Controller
的 viewWillAppear
里添加了这样的方法:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// 设置navigaitonControllerDelegate
self.navigationController.delegate = self;
// 隐藏状态栏
[UIView animateWithDuration:0.2 animations:^{
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
}];
}
之后,tabbar
和statusBar
的动画效果都有了,具体原因我才疏学浅,不太懂,感觉像是tabbar
和statusBar
的动画是绑定的,或者他们俩之间同时修改会有冲突。
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;
}
}
- 设置
tableView
的最小缩放大小是0.8,小于0.8就执行pop。 - 结束手势的时候回到初始大小。
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