iOS开发之Pop框架(二)
来源:互联网 发布:网络彩票牌照发放2家 编辑:程序博客网 时间:2024/05/29 17:24
本周早些时候,Ole Begemann写了一个很棒的教程:“UIScrollView是如何工作的”。并且作了详细解释,他甚至从头开始创建了一个非常简单的滚动视图。
创建过程很简单:使用UIPanGestureRecognizer,然后改变边界的原点来响应拖拽手势的转化。
扩展Ole的自定义滚动视图,以包含UIScrollView内部惯性滚动似乎很自然,使用Facobook最近发布的Pop动画引擎,我认为编写一个减速自定义滚动视图将是一个很有意思的项目。
Pop是Facebook当前使用的开源动画框架,承载了Paper中所有的动画和交互。Pop的API与CoreAnimation很相似,所以并不难学习。
Pop中两个很好的地方:
1.除了基本的动画外,Pop还提供spring和decay动画;
2.你可以使用Pop对NSObject对象上的任何属性执行动画效果,而不仅仅是UIKit中声明的动画属性。
由于Pop内置支持decaying动画,实现UIScrollView的声明就不那么困难了。
主要思想是在动画结束时,从UIPanGestureRecognizer中去掉速度属性,以减缓的方式来设置边界原点的动画。
那么让我们开始吧!首先先不考虑Ole的CustomScrollView项目,先看看handlePanGesture--CustomScrollView.m. 的方法(为什么要先看我的项目而不是Ole的呢?因为,每次激发手势时,Ole都会将translation设置为零,这就重置了速度。然而,我们是想保存速度)。
我们想要在手势完成之后开始一个decay动画,因此我们对switch语句中的UIGestureRecognizerStateEnded实例很感兴趣。
以下是嵌入代码片段的基本流程:
我们使用velocityInView: 方法获得手势的速度:
CGPoint velocity = [panGestureRecognizer velocityInView:self];
如果没有足够的水平或者垂直内容,我们不希望在x或者y方向发生任何移动。因此,我们添加判断语句:
if (self.bounds.size.width >= self.contentSize.width) { velocity.x = 0; } if (self.bounds.size.height >= self.contentSize.height) { velocity.y = 0; }
事实证也证明,我们使用velocityInView: 方法获得的速度是我们真正想要的负值,所以我们来解决这个问题。
我们想要为边界设置动画(尤其是边界的原点),因此我们使用kPOPViewBounds属性创建一个新的POPDecayAnimation。
POPDecayAnimation *decayAnimation = [POPDecayAnimation animationWithPropertyNamed:kPOPViewBounds];
Pop希望这个速度的坐标空间与你打算设置动画的属性的坐标空间相同。这个值将会是alpha(CGFloat)的一个NSNumber,CGPoint封装在NSValue中作为中心值,
CGRect封装在NSValue中作为边界值。正如你或许已经猜到了,在动画期间,属性值的改变是相应的速度分量的一个因素。
在我们的例子中,为边界设置动画意味着速度将是一个CGRect,它的origin.x值和origin.y值对应边界原点的变化。同样地,size.width速度和size.height速度对应边界大小的变化。
我们不想改变边界的大小,所以我们将它的速度分量设置为0。将拖拽手势的速度分别赋值给origin.x和origin.y。将整个事情封装到NSValue中,然后赋值给decayAnimation.velocity。
decayAnimation.velocity = [NSValue valueWithCGRect:CGRectMake(velocity.x, velocity.y, 0, 0)];
最后,使用pop_addAnimation: 方法和你想用的任意键,将decayAnimation 添加到视图上。
[self pop_addAnimation:decayAnimation forKey:@"decelerate"];
以下是整合后的代码:
//get velocity from pan gestureCGPoint velocity = [panGestureRecognizer velocityInView:self];if (self.bounds.size.width >= self.contentSize.width) { //make movement zero along x if no horizontal scrolling velocity.x = 0; }if (self.bounds.size.height >= self.contentSize.height) { //make movement zero along y if no vertical scrolling velocity.y = 0; }//we need the negative velocity of what we get from the pan gesture, so flip the signsvelocity.x = -velocity.x;velocity.y = -velocity.y;POPDecayAnimation *decayAnimation = [POPDecayAnimation animationWithPropertyNamed:kPOPViewBounds];//last two components zero as we do not want to change bound's sizedecayAnimation.velocity = [NSValue valueWithCGRect:CGRectMake(velocity.x, velocity.y, 0, 0)];[self pop_addAnimation:decayAnimation forKey:@"decelerate"];
使用这个你应该能获得减速的基本功能。你将注意到,如果速度很高的话,视图滚动时会超出contentSize。但是,这个事情很好解决,只要重载setBound:并添加判断语句来检查边界原点没有超出阈值。我很惊讶的是Pop并没有自动添加这个判断。
Pop中一个真正有趣的部分是,它能够为所有的属性设置动画,而不仅仅是UIKit声明的动画属性。既然我们只为边界远点设置动画,我认为这是一个很好的计划去尝试Pop自定义属性动画。
通过在动画进程中发送固定的回调,Pop允许你做这件事情,因此,你可以相应地更新你的视图。Pop处理了最难的部分--时间处理,将时间转换成属性值,是动画减缓或者震荡等。
这里你做的事情跟我们之前做的事情是一样的,不同的是现在你使用的是自定义属性。尤其是我们将动画bounds.origin.x和bounds.origin.y。你会看到代码结构大体上没有变化,除了这个巨大的POPAnimatableProperty属性的初始化。
就我个人理解,属性的名称可以是任意的字符串。在这个例子中,我命名为:
@"com.rounak.bounds.origin"
还有一个将POPMutableAnimatableProperty作为参数的初始化block(我认为这个初始化模式可以称为builder pattern)。
POPMutableAnimatableProperty有两个重要属性:readBlock 和 writeBlock。在readBlock中,你需要向Pop中传数据,而在writeBlock中你需要检索这个数据,并更新你的视图。readBlock值调用一次,而writeBlock在每一帧更新显示的时候都会被调用(通过CADisplayLink)。
Pop内在地将所有变量转化为矢量,因此它以CGFloat的线性数组的形式请求和发送数据。
在readBlcok中,你只需分别地将bounds.origin.x和bounds.origin.y分配给 values[0] 和 values[1]。
prop.readBlock = ^(id obj, CGFloat values[]) { values[0] = [obj bounds].origin.x; values[1] = [obj bounds].origin.y;};
在writeBlock中,你需要读取values[0] (bounds.origin.x) 和 values[1] (bounds.origin.y) ,并更新你的视图边界。
prop.writeBlock = ^(id obj, const CGFloat values[]) { CGRect tempBounds = [obj bounds]; tempBounds.origin.x = values[0]; tempBounds.origin.y = values[1]; [obj setBounds:tempBounds];};
writeBlock中有神奇的事情发生了,每次使用值调用writeBlock都会遵循一个减缓(或振动)曲线。
你会注意到,由于这里我们只是在二维中操作,我们的速度是CGPoint,而不是CGRect。
以下是整合后的代码:
//get velocity from pan gestureCGPoint velocity = [panGestureRecognizer velocityInView:self];if (self.bounds.size.width >= self.contentSize.width) { //make movement zero along x if no horizontal scrolling velocity.x = 0;}if (self.bounds.size.height >= self.contentSize.height) { //make movement zero along y if no vertical scrolling velocity.y = 0;}//we need the negative velocity of what we get from the pan gesture, so flip the signsvelocity.x = -velocity.x;velocity.y = -velocity.y;POPDecayAnimation *decayAnimation = [POPDecayAnimation animation];POPAnimatableProperty *prop = [POPAnimatableProperty propertyWithName:@"com.rounak.boundsY" initializer:^(POPMutableAnimatableProperty *prop) { // read value, feed data to Pop prop.readBlock = ^(id obj, CGFloat values[]) { values[0] = [obj bounds].origin.x; values[1] = [obj bounds].origin.y; }; // write value, get data from Pop, and apply it to the view prop.writeBlock = ^(id obj, const CGFloat values[]) { CGRect tempBounds = [obj bounds]; tempBounds.origin.x = values[0]; tempBounds.origin.y = values[1]; [obj setBounds:tempBounds]; }; // dynamics threshold prop.threshold = 0.01;}];decayAnimation.property = prop;decayAnimation.velocity = [NSValue valueWithCGPoint:velocity];[self pop_addAnimation:decayAnimation forKey:@"decelerate"];
0 0
- iOS开发之Pop框架(二)
- iOS开发之Pop框架(一)
- iOS开发之Pop框架(三)
- iOS-MultipeerConnectivity框架开发(二)
- 【Facebook的UI开发框架React入门之二】开发环境搭建(iOS平台)-goodmao
- IOS 动画框架pop使用方法
- iOS 开发之照片框架详解之二 —— PhotoKit 详解(上)
- iOS 开发之照片框架详解之二 —— PhotoKit 详解(下)
- iOS 开发之照片框架详解之二 —— PhotoKit 详解(上)
- iOS 开发之照片框架详解之二 —— PhotoKit 详解(下)
- iOS 开发之照片框架详解之二 —— PhotoKit 详解(上)
- iOS 开发之照片框架详解之二 —— PhotoKit 详解(下)
- iOS开发-------3D Touch之Peek,Pop,UIPreviewAction
- IOS开发系列—Objective-C之Foundation框架(二)
- iOS开发框架介绍(二)---iOS 框架及开发相关信息
- iOS开发框架介绍(二)---iOS 框架及开发相关信息
- iOS开发之自动布局(二)
- iOS 开发之ASIHttpRequest(二)
- POJ 3311 Hie with the Pie (状压DP)
- iOS 日常工作之常用宏定义大全
- QlikView 笔记(五) 通过Partial Reload实现增量抽取
- 球衣退役
- IOS开发-cocoaPod安装遇到的错误以及解决方法
- iOS开发之Pop框架(二)
- TrueCrypt代码 之 几个文件操作函数,经常会用到
- C语言getpwuid()函数:从密码文件中取得指定uid的数据
- Labelling Unsegmented Sequence Data with Recurrent Neural Networks(笔记)
- 格式化字符串长度不够补0
- Cocos2d-x 纹理缓存(Texture Cache)
- PHP如何获取客户端真实IP地址?
- Java in Nutshell 摘要
- 隐写术