Pop上手体验(i-v)

Pop上手体验(i)
Facebook一直为开发者提供自己的开源代码库非常令人感激。最新的一个是Pop,在Github上不到24小时就已经获得3500个星了(目前是将近6000个)。
 (文中涉及动态图,可能会加载的慢,请耐心查看!)
 
Facebook官方阐述:
 Pop是一个适用于iOS和OS X平台的可扩展动画引擎。除了基本的静态动画,Pop还支持spring和decay动画,有助于打造一个逼真的,基于物理的交互。你可以通过Pop的API把Pop快速集成到现有的Objective-C代码库中,并在任何对象上实现动画的任何属性。这是一个成熟的并且经过良好测试的框架,承载了Paper中所有的动画和交互。
 
我使用Pop创建一个非常简单的例子。我只是想看看它是如何很好地实现用户输入框带有的阴影效果,它确实做得很棒。我还想快速的创建我所知道的东西。在使用POPSpringAnimation这个例子中,我觉得这个代码跟我写过的其他代码很相似。
 
至于研究这个库,我的策略是查阅这些文件中的.h文件(这库也有Objective-C++版本):
POPBasicAnimation
POPDecayAnimation
POPPropertyAnimation
POPSpringAnimation
POPCustomAnimation
POPAnimation
POPAnimatableProperty
 
Pop的一些东西确实很酷,即当你添加一个动画时,展示层和模型层是同步的。Sam Page(@sampage)中使用Pop的圆圈例子就是这样。由于strokeEnd和strokeStart不是默认属性的一部分,所以你需要创建自己的自定义属性(不过我可能错失了什么),如下:
  1. [POPAnimatableProperty propertyWithName:@"strokeStart" initializer:^(POPMutableAnimatableProperty *prop) { 
  2.         prop.readBlock = ^(id obj, CGFloat values[]) { 
  3.             values[0] = [obj strokeStart]; 
  4.         }; 
  5.         prop.writeBlock = ^(id obj, const CGFloat values[]) { 
  6.             [obj setStrokeStart:values[0]]; 
  7.         }; 
  8.     }]; 
 
不得不说这个很强大,正如我之前所说的:表现层和模型层是同步的。
 
Pop上手体验(ii)
Facebook Paper Tech Talk中让我困惑的是Brian Amerige(@brianamerige)的一些话,尤其是他向我们示范如何跨过手势和动画之间的间隙(@ minute 47:40)。直接从视频中获得的信息:
 基于手势速度旋转UIView:
  1. - (void)rotate:(UIPanGestureRecognizer*)recognizer 
  2.     CGPoint velocity = [recognizer velocityInView:self.view]; 
  3.  
  4.     POPSpringAnimation *spring = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerRotation]; 
  5.     spring.velocity = [NSValue valueWithCGPoint:velocity]; 
  6.  
  7.     [_outletView.layer pop_addAnimation:spring forKey:@"rotationAnimation"]; 
 
现在,当你开始上手体验时一切变得非常有趣:
 
当位置、大小和"dynamics"一同作用的时候会发生怎样的事情?
代码:
  1. - (void)rotate:(UIPanGestureRecognizer*)recognizer 
  2.     CGPoint velocity = [recognizer velocityInView:self.view]; 
  3.  
  4. POPSpringAnimation *positionAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPosition];   
  5. positionAnimation.velocity = [NSValue valueWithCGPoint:velocity];   
  6. positionAnimation.dynamicsTension = 5;   
  7. positionAnimation.dynamicsFriction = 5.0f;   
  8. positionAnimation.springBounciness = 20.0f;   
  9. [_outletView.layer pop_addAnimation:positionAnimation forKey:@"positionAnimation"]; 
  10.  
  11.  
  12. POPSpringAnimation *sizeAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerSize];   
  13. sizeAnimation.velocity = [NSValue valueWithCGPoint:velocity];   
  14. sizeAnimation.springBounciness = 1.0f;   
  15. sizeAnimation.dynamicsFriction = 1.0f;   
  16. [_outletView.layer pop_addAnimation:sizeAnimation forKey:@"sizeAnimation"]; 
 
移除与"dynamics"相关的代码后:
你仍然能看到轻微的弹跳效果,但这是POPSpringAnimation的默认值。
  
Pop上手体验(iii)
Pop或者任何其他出于消遣目的的库都会涉及到一点--我应该用它来做什么?在Paper by Facebook这篇文章中,作者Brian Lovin (@brian_lovin)用动态图展示了Paper的设计细节,可以帮忙进行思考。下图来自Brian Lovin的博客(这里有该博客的译文:23个Facebook Paper中的设计细节 ):
  
我想创建可以展示小弹窗的东西,但还要带一点震动效果(好吧,因为我喜欢)。这个描述可能不是很准确(甚至相差甚远),但它给了我一点灵感,所以:
 
你也看的出来,它似乎不是那么迷人,但是用Pop很容易做出来。
  1. - (void)hidePopup 
  2.     _isMenuOpen = NO; 
  3.     POPBasicAnimation *opacityAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity]; 
  4.     opacityAnimation.fromValue = @(1); 
  5.     opacityAnimation.toValue = @(0); 
  6.     [_popUp.layer pop_addAnimation:opacityAnimation forKey:@"opacityAnimation"]; 
  7.  
  8.     POPBasicAnimation *positionAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPosition]; 
  9.     positionAnimation.fromValue = [NSValue valueWithCGPoint:VisiblePosition]; 
  10.     positionAnimation.toValue = [NSValue valueWithCGPoint:HiddenPosition]; 
  11.     [_popUp.layer pop_addAnimation:positionAnimation forKey:@"positionAnimation"]; 
  12.  
  13.     POPSpringAnimation *scaleAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 
  14.  
  15.     scaleAnimation.fromValue  = [NSValue valueWithCGSize:CGSizeMake(1.0f, 1.0f)]; 
  16.     scaleAnimation.toValue  = [NSValue valueWithCGSize:CGSizeMake(0.5f, 0.5f)]; 
  17.     [_popUp.layer pop_addAnimation:scaleAnimation forKey:@"scaleAnimation"]; 
 
展示:
  1. - (void)showPopup 
  2.     _isMenuOpen = YES; 
  3.  
  4.     POPBasicAnimation *opacityAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity]; 
  5.     opacityAnimation.fromValue = @(0); 
  6.     opacityAnimation.toValue = @(1); 
  7.     opacityAnimation.beginTime = CACurrentMediaTime() + 0.1; 
  8.     [_popUp.layer pop_addAnimation:opacityAnimation forKey:@"opacityAnimation"]; 
  9.  
  10.     POPBasicAnimation *positionAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPosition]; 
  11.     positionAnimation.fromValue = [NSValue valueWithCGPoint:VisibleReadyPosition]; 
  12.     positionAnimation.toValue = [NSValue valueWithCGPoint:VisiblePosition]; 
  13.     [_popUp.layer pop_addAnimation:positionAnimation forKey:@"positionAnimation"]; 
  14.  
  15.  
  16.     POPSpringAnimation *scaleAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 
  17.     scaleAnimation.fromValue  = [NSValue valueWithCGSize:CGSizeMake(0.5, 0.5f)]; 
  18.     scaleAnimation.toValue  = [NSValue valueWithCGSize:CGSizeMake(1.0f, 1.0f)];//@(0.0f); 
  19.     scaleAnimation.springBounciness = 20.0f; 
  20.     scaleAnimation.springSpeed = 20.0f; 
  21.     [_popUp.layer pop_addAnimation:scaleAnimation forKey:@"scaleAnimation"]; 
 
三条注意事项:
1. 当添加类似[myView pop_addAnimation:animation forKey:@"animationKey"];的动画时,如果你用相同的key添加其他动画,那么新添加的动画将会取代先前的动画。
2. 当你想要开始一个动画时,使用CACurrentMediaTime()生成一个动画开始时间来初始化beginTime。所以它看起来应该像:animation.beginTimee = CACurrentMediaTime() + delayInSeconds;.我简单地添加了delay,当然不会凑效。感谢Kimon(@kimon) 的警告。
3. 当你看到类似kPOPLayerScaleXY属性时,它将会有两个值。在这个例子中是CGSize。现在可能是有意义的,不过我传递了一个NSNumber(单一值),期待设置成X和Y值。
 
Pop上手体验(iv)
这是天才 (via @_tiagoalmeida):
 
还真有用:
  1. POPAnimatableProperty *constantProperty = [POPAnimatableProperty propertyWithName:@"constant" initializer:^(POPMutableAnimatableProperty *prop){   
  2.         prop.readBlock = ^(NSLayoutConstraint *layoutConstraint, CGFloat values[]) { 
  3.             values[0] = [layoutConstraint constant]; 
  4.         }; 
  5.         prop.writeBlock = ^(NSLayoutConstraint *layoutConstraint, const CGFloat values[]) { 
  6.             [layoutConstraint setConstant:values[0]]; 
  7.         }; 
  8.     }]; 
  9.  
  10. POPSpringAnimation *constantAnimation = [POPSpringAnimation animation];   
  11. constantAnimation.property = constantProperty;   
  12. constantAnimation.fromValue = @(_layoutConstraint.constant);   
  13. constantAnimation.toValue = @(200);   
  14. [_layoutConstraint pop_addAnimation:constantAnimation forKey:@"constantAnimation"]; 
 
感谢Jake Marsh (@jakemarsh).
 
这是一个小便签,我没有注意到kPOPLayoutConstraintConstant,所以你无需创建一个自定义POPAnimatableProperty。
  
Pop上手体验 (v)
在上手体验Pop几天后,有一点就是除了享受它,我应该做一点贡献。
 
在该系列的第一篇中,我为strokeStart和strokeEnd (两者均属于CAShapeLayer)创建了自定义属性:
  1. [POPAnimatableProperty propertyWithName:@"strokeStart" initializer:^(POPMutableAnimatableProperty *prop) { 
  2.         prop.readBlock = ^(id obj, CGFloat values[]) { 
  3.             values[0] = [obj strokeStart]; 
  4.         }; 
  5.         prop.writeBlock = ^(id obj, const CGFloat values[]) { 
  6.             [obj setStrokeStart:values[0]]; 
  7.         }; 
  8.     }]; 
 
这个过程有点工作量,但不用害怕。我的第一个pull request(希望不是最后一个)已经通过审核,并并入了主要的Pop repo。这意味着现在我让这两个属性应用在了CAShapeLayer上,没有添加任何逻辑。
 
简单几步即可为Pop添加属性,如果有人想要贡献的话,可以:
1.把NSString和你的属性名称添加到POPAnimatableProperty.h中,遵守它的命名惯例,看起来可能像kPOP<class name witout prefix><propertyName>。如果它不止有一个值,那么它可能会像kPOP<class name witout prefix><propertyName>XY。然后在POPAnimatableProperty.m上添加实际值,它可能会是NSString * const kPop<class name witout prefix><propertyName> = @"<propertyName>"。如果你不确定如何命名,可以看看其他属性。
2.添加write/read blocks有效的方法,加上阀值。你可以看看其他属性是怎么做的
3.我不需要这么做,因为我添加的属性非常简单。当读/写新值的时候,充分利用辅助属性会好很多。比如你可以看看kPOPViewBackgroundColor是如何实现的:
  1. {kPOPViewBackgroundColor, 
  2.   ^(UIView *obj, CGFloat values[]) { 
  3.     POPUIColorGetRGBAComponents(obj.backgroundColor, values); 
  4.   }, 
  5.   ^(UIView *obj, const CGFloat values[]) { 
  6.     obj.backgroundColor = POPUIColorRGBACreate(values); 
  7.   }, 
  8.   1.0 
  9. }, 
 
这个例子使用了POPUIColorGetRGBAComponents和POPUIColorRGBACreate:
  1. void POPUIColorGetRGBAComponents(UIColor *color, CGFloat components[])   
  2.   return POPCGColorGetRGBAComponents(color.CGColor, components); 
  3.  
  4. UIColor *POPUIColorRGBACreate(const CGFloat components[])   
  5.   CGColorRef colorRef = POPCGColorRGBACreate(components); 
  6.   UIColor *color = [[UIColor alloc] initWithCGColor:colorRef]; 
  7.   CGColorRelease(colorRef); 
  8.   return color; 
 
这个辅助方法位于POPCGUtils上,虽然POPLayerExtras上有很多。作为一个“良好公民”,你可以创建其他方法,所以用户可把它们用于其他相似的属性行为。
1. 为test suit添加你的属性!由于那些属性暗昧不明,所以我仅把它添加到了POPAnimatablePropertyTests.m的testProvidedExistence,以确保它的实现是确实存在的。
2. 如果你做了与众不同的事情,并且没有覆盖默认的test suit,那么你需要更多的测试。
 
随着我的需求的增长,我将会为Pop贡献更多。
展开阅读全文

没有更多推荐了,返回首页