start
冒个泡,体现下存在感,哈哈哈哈,最近发现iOS自带的动画效果总是困扰着我-,-比如,iOS7上返回时,会自动加一个半透明的层做渐变感,比如返回的时候,总是整个view一起动的,动画过程中,我需要隐藏和显示导航栏等这些要求,原生的动画总是无法满足这些小小的要求!(ノ`□´)ノ⌒┻┻
经过数小时上网找资料,发现大神们要不就是一笔带过,要不就是懒得讲很多,于是,发现还是自己写比较科学啊┓( ̄∇ ̄)┏,下面我们来尝试做一个
做的方法
制作方法还是按照古法炮制,继承一个NSNavigationController
然后重写其中的pop和push方法以达到我们的目的,在我的理解,导航控件他的pop和push方法,实现了在他自己的控制器栈中实现不断的出栈和入栈的操作,然后播放一小段动画,愉悦下大家,那么我们要做的事情就是去掉原来的动画,做pop,push的动作,然后播放我们自己的动画
截图工具
做动画之前,必须要有的一个东西就是,我们要让什么东西来动呢,那导航上必然是两个ViewController中的View的内容喽,那么我们就需要对UIView
进行扩充,实现获取View中的内容。
根据观察研究,发现有4个函数能够满足我们的需求,他们分别是:
- (void)renderInContext:(CGContextRef)ctx
- (BOOL)drawViewHierarchyInRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates
- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates
- (UIView *)resizableSnapshotViewFromRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates withCapInsets:(UIEdgeInsets)capInsets
这两个方法都是UIView
提供的方法,第二个以后的方法都是iOS7以后提供的方法,第一个方法,由于对于毛玻璃等高端酷炫的效果支持不太好(容易出现黑块)并且我们都面向iOS7吧,所以就不用了。后面两个snapshot的方法,会根据当前的视图创建一个snapshot的视图(静态的,啥也不能操作)但是这两个方法没办法处理正在进行中的动画和比较复杂的效果(其实就是毛玻璃啦)。
所以,我就用drawViewHierarchyInRect:afterScreenUpdates
这个方法喽。
嘛,为了各种模块化啊,方便啊,之类的,我们就做一个category啦,就像下面的样子
1 | @implementation UIView (Snapshot) |
上面『看这里,看这里』后面的那一段大家会觉得比较奇怪吧,为啥要多截取一次呢。。。
原因是,发现如果view还未显示,也就是将要push的view,他的size是错误的,只有在完成了drawViewHierarchyInRect:afterScreenUpdates
方法调用后,才会得到正确的大小,如果用错误的大小渲染的话,得到的结果往往是拉伸的截图。所以在这里,截取第一次以后,发现view的size发生变化的话,就重新再截取一次。
尝试过调用viewDidLoad
,layoutSubviews
,needLayoutSubviews
之类的方法,但是都没有效果,所以这里只是一种替代的方法了,如果大家有更好的方法,也可以告诉我怎么来做。
视图的切换
OK既然截图的工具有了,那么下一步就是完成视图切换的动作了,我们要做的是,截图->切换->截图->放动画,那么顺着来吧
实现切换
下面的内容将把pushViewController:animated:
方法完全拆开,一步一步告诉大家怎么来做
第一步,如果这个导航操作本来就不需要动画,那就直接跳过了,这一步push和pop的行为都是一样的
if (!animated) {
[super pushViewController:viewController animated:animated];
return;
}
接下来,我们在切换之前需要对当前的视图进行截图,用来做动画的素材
UIViewController *currentVC = self.topViewController;
UIImage *topImage = [currentVC.view snapshot];
完成了之后,我们就悄悄的完成我们的push操作,为啥是悄悄的呢-,-因为我们是不用动画的push
[super pushViewController:viewController animated:NO];
这样就完成了push的动作,完成push的动作后,下一步自然是截取新的截图
UIImage *nextImage = [self.topViewController.view snapshot];
self.topViewController.view.hidden = YES;
上面hidden刚push的view是非常必要的,因为我们在调用super
的pushViewController:animated:
方法后,新的view可以说已经是在屏幕显示的时候就绘制好了,这样他还一直显示着的话,会在我们的动画背景下面显示个他自己,所以这里我先把它隐藏了,等动画放完了,再显示
完成了动画素材的准备,接下来,我们就放动画愉悦下观众们了,这里我的动画就非常简单粗糙了,就准备左右移动下啦,哈哈哈哈,毕竟我写的动画太丑太low了┓( ̄∇ ̄)┏以及我的动画过程也很丑,这里是大家发挥自己想象力的地方了
UIImageView *topImageView = [[UIImageView alloc] initWithImage:topImage];
UIImageView *nextImageView = [[UIImageView alloc] initWithImage:nextImage];
[self.view addSubview:topImageView];
[self.view addSubview:nextImageView];
CGRect frame = nextImageView.frame;
frame.origin.x += frame.size.width;
nextImageView.frame = frame;
CGRect frameTopPos2 = topImageView.frame;
frameTopPos2.origin.x -= frame.size.width;
CGRect frameNextPos2 = nextImageView.frame;
frameNextPos2.origin.x = 0.0f;
[UIView animateWithDuration:2 animations:^{
topImageView.frame = frameTopPos2;
nextImageView.frame = frameNextPos2;
} completion:^(BOOL finished) {
self.topViewController.view.hidden = NO;
[topImageView removeFromSuperview];
[nextImageView removeFromSuperview];
}];
嗯嗯,这丑到爆的动画就完成了,希望大家多画点花在自己的切换动画上ヾ(´▽`; )ノ
pop的方法也比较类似这里就不浪费大家时间了,只是需要注意的是,pop出来的ViewController需要作为pop方法的返回值
end
有始有终,发现好久没写了,刚好把最近开发遇到的种种奇葩和悲剧的替代方案都拿出来讲讲了,画图那个-,-慢慢写喽,就酱
最后欢迎大家订阅我的微信公众号 Little Code
- 公众号主要发一些开发相关的技术文章
- 谈谈自己对技术的理解,经验
- 也许会谈谈人生的感悟
- 本人不是很高产,但是力求保证质量和原创