在上一篇文章中,我们已经能用JazzHands做出一个完整的 Guide 动画了,但是这个动画还有一些不足.
- 它是用 Frame 来布局的,在不同的机型上适配起来很麻烦
JazzHands 2.0已经解决了这个问题
用 AutoLayout 来布局,用 IFTTTConstraintConstantAnimation 和 IFTTTConstraintMultiplierAnimation来做动画
使用起来代码会复杂一些,但是对屏幕适配,屏幕旋转, iPad split-screen 支持的更好.
使用 IFTTTAnimatedPagingScrollViewController
IFTTTAnimatedPagingScrollViewController
内部Override 下面这2个方法,结合 AutoLayout 完成适配.
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration:
接下来我们使用AutoLayout 和 JazzHands做一个简单的 透明度渐变动画.
创建一个空 ViewController 继承 IFTTTAnimatedPagingScrollViewController
这是 JazzHands 提供给我们的方便结合使用 AutoLayout 制作动画的类,以后我们使用 AutoLayout 做动画直接继承这个类就可以了,它已经帮我们初始化好了 scrollView,contentView,Animator
在类扩展中只需添加我们动画的 View
@interface JazzHandsPageViewControllerDemo ()
@property (strong,nonatomic) UIView *v;
@end
在 ViewDidLoad 中
- (void)viewDidLoad {
[super viewDidLoad];
self.v=[UIView new];
self.v.backgroundColor=[UIColor redColor];
[self.contentView addSubview:self.v]; //注意添加 view到 contentView 中
self.scrollView.showsHorizontalScrollIndicator=YES;
self.scrollView.backgroundColor=[UIColor whiteColor];
self.edgesForExtendedLayout = UIRectEdgeNone; //ios7以后 NavigationBar 会遮住内容,加上这行保证内容显示在 navigationBar 下面,一般来说我们的 Guide 动画都是全屏的,不需要加这行
[self.v mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.height.equalTo(@30); //固定 view 宽高为30
}];
[self keepView:self.v onPage:0]; //keepView 下面会说到
IFTTTAlphaAnimation *alpha=[IFTTTAlphaAnimation animationWithView:self.v];
[self.animator addAnimation:alpha];
[alpha addKeyframeForTime:0 alpha:1];
[alpha addKeyframeForTime:1 alpha:0]; //注意 time 是 1
}
运行一下,效果如图
上面的代码和我们前2篇文章使用 Frame 布局制作动画类似,但有3点不同.
- 将 View 添加到了 contentView中.
- Masonry 给 view 布局
- addKeyframeForTime:1 time 是1.
关于第一点 : 将 View 添加到了 contentView中.
引用一段 斯坦福大学公开课:iOS 7应用开发
gif 中手机屏幕是全屏的 scrollView, 后面的图片是 contentView, 我们将contentView 用scrollView.addsubView(contentView)
, 添加到 scrollView 中,再将其他所有需要展示的 View 添加到 contentView 中, scrollView 是为后面的 ContentView提供一个窗口
我们手指滑动屏幕, scrollView.contentOffset.x
改变,我们能看到的 contentView 的内容看起来跟着滚动变化了,但实际上 contentView 的位置是固定的.
我们在继承 IFTTTAnimatedPagingScrollViewController
时contentView,scrollView
已经被初始化好了,我们只需将需要展示的 view 添加到 self.contentView
中即可
关于第二点
既然我们使用 AutoLayout, 那么对 view 进行布局,设置动画,就都要使用 AutoLayout, 这里使用 Mansonry 开源库来设置约束.
关于第三点 : addKeyframeForTime:1 time 是1.
之前我们使用 Frame
//创建一个透明度动画
IFTTTAlphaAnimation *alpha=[IFTTTAlphaAnimation animationWithView:self.v];
//将所有动画添加到 animator 中
[self.animator addAnimation:alpha];
//添加2个帧,在 scrollView 从 0 滚动到 200 时, 淡出我们的 View
[alpha addKeyframeForTime:0 alpha:1.0];
[alpha addKeyframeForTime:200 alpha:0.0];
//scrollView 代理
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
[self.animator animate:scrollView.contentOffset.x];
}
}
现在我们使用 AutoLayout 和 IFTTTAnimatedPagingScrollViewController
IFTTTAlphaAnimation *alpha=[IFTTTAlphaAnimation animationWithView:self.v];
[self.animator addAnimation:alpha];
[alpha addKeyframeForTime:0 alpha:1];
[alpha addKeyframeForTime:1 alpha:0];
IFTTTAnimatedPagingScrollViewController
内部帮我们做了scrollView.contentOffset.x
和time
的转换
具体实现类似 :
time = self.scrollView.contentOffset.x / self.pageWidth;
它将 contentOffset.x转换为 页面宽度的百分比,
[alpha addKeyframeForTime:0 alpha:1];
[alpha addKeyframeForTime:1 alpha:0];
所以这段代码我们设置的动画是, scrollView 从第一页滚动到第二页, view 的透明度从1变化到0
[keepView:onPage:]方法
如果你细心的话,可以发现上面的例子中,我们并没有设置 x 轴的约束,但是红色方块的位置在 X 轴居中,
就是因为[self keepView:self.v onPage:0];
,这行代码
我们写一个例子来看看这个方法及其重载方法的作用,
新建一个空白 ViewController, 继承自 IFTTTAnimatedPagingScrollViewController
类扩展中
@interface JazzHandsKeepViewDemo ()
@property (strong,nonatomic) UIImageView *ifttt;
@end
ViewDidLoad 中
- (void)viewDidLoad {
[super viewDidLoad];
self.ifttt=[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"IFTTTPresents"]];
self.scrollView.backgroundColor=[UIColor greenColor];
self.scrollView.showsHorizontalScrollIndicator=YES;
[self.contentView addSubview:self.ifttt];
self.edgesForExtendedLayout = UIRectEdgeNone;
[self keepView:self.ifttt onPage:0];
}
我们还需要 Override 一个方法,告诉父类我们需要的页数
-(NSUInteger)numberOfPages{
return 4;
}
运行一下,效果如图
我们并没有给 ITFFF imageView设置任何的动画和约束,但是它在 X轴居中,
就是因为我们设置了[self keepView:self.ifttt onPage:0];
,让 imageView 出现在第一页,
我们继续使用它的重载方法..
[self keepView:self.ifttt onPages:@[@(0),@(1)]];
运行结果如下..
注意看下面的 scrollIndicator, 即使我滑动到第二页,它还是保持在屏幕中间的位置,
继续修改代码为
[self keepView:self.ifttt onPages:@[@(0),@(1)] atTimes:@[@(0),@(1)]];
这个运行的结果和上面一样,其实我们调用 [self keepView:self.ifttt onPages:@[@(0),@(1)]];
时,
内部会调用它的重载方法
[self keepView:self.ifttt onPages:@[@(0),@(1)] atTimes:@[@(0),@(1)]];
,
所以这2个方法的效果是一样的
再次修改代码,最后一次了...
[self keepView:self.ifttt onPages:@[@(0),@(1.5)] atTimes:@[@(0),@(1)]];
运行结果如下
这个运行结果看起来有点诡异,
先来解释这个方法,
[self keepView:self.ifttt onPages:@[@(0),@(1)] atTimes:@[@(0),@(1)]];
调用这个方法起到的效果是, ImageView 会保持在第一页和第二页的中间,
当我们调用这个方法时,内部会给 imageView 和 contentView 添加一个 X 轴的约束,让 imageView 保持在页面中间,
onPages:@[@(0),@(1)]
就代表保持在第一二页的中间, atTimes:@[@(0),@(1)]
代表2个时间点,
它内部的实现是在 scrollView 滚动时,将 scrollView.contentOffset.x 累加给 imageView 和 contentView 的约束的 constant 上,也就是 scrollView 滚动多少, imageView 也滚动多少,所以产生的效果就是 imageView 一直保持在第一页和第二页的中间不动.
别忘了 JazzHands 是一个帧动画,所以我们设置参数的解释就是,在 time=0时, imageView 保持在第一页中间, time=1时, imageView 保持在第二页中间,我们只需设置这2个关键帧,其余的 JazzHands 会帮我们搞定.
那么下面这个方法呢?..
[self keepView:self.ifttt onPages:@[@(0),@(1.5)] atTimes:@[@(0),@(1)]];
在 time=0时, imageView 保持在第一页中间,在 time=1时, imageView 保持在第二页中间 再偏离中间0.5倍的位置.
具体的实现是,现在 scrollView 滚动10px, imageView 滚动 15px, 所以从第一页滚动到第二页的过程中,感觉 imageView 滚动的距离比较长,滚动的比较快.
所以[self keepView:self.ifttt onPages:@[@(0),@(1.5)] atTimes:@[@(0),@(1)]];
这个方法不仅能将 view 保持在某一页面出现,调整参数后,还能制作 X 轴偏移的动画,所以后我们给 view设置约束时,不用设置 X 轴的约束,只需调用此方法即可约束 view 的 X 轴位置.
明白了这点就可以无压力的制作动画了..
Done
所有代码你可以在 Github 中找到
下一篇 我们用 JazzHands+AutoLayout 来模仿 官方 Demo
的效果