Pop上手體驗(i)html
Facebook一直爲開發者提供本身的開源代碼庫很是使人感激。最新的一個是Pop,在Github上不到24小時就已經得到3500個星了(目前是將近6000個)。git
(文中涉及動態圖,可能會加載的慢,請耐心查看!)github
Facebook官方闡述:spring
Pop是一個適用於iOS和OS X平臺的可擴展動畫引擎。除了基本的靜態動畫,Pop還支持spring和decay動畫,有助於打造一個逼真的,基於物理的交互。你能夠經過Pop的API把Pop快速集成到現有的Objective-C代碼庫中,並在任何對象上實現動畫的任何屬性。這是一個成熟的而且通過良好測試的框架,承載了Paper中全部的動畫和交互。 |
我使用Pop建立一個很是簡單的例子。我只是想看看它是如何很好地實現用戶輸入框帶有的陰影效果,它確實作得很棒。我還想快速的建立我所知道的東西。在使用POPSpringAnimation這個例子中,我以爲這個代碼跟我寫過的其餘代碼很類似。app
至於研究這個庫,個人策略是查閱這些文件中的.h文件(這庫也有Objective-C++版本):框架
POPBasicAnimationide
POPDecayAnimation測試
POPPropertyAnimation動畫
POPSpringAnimationui
POPCustomAnimation
POPAnimation
POPAnimatableProperty
Pop的一些東西確實很酷,即當你添加一個動畫時,展現層和模型層是同步的。Sam Page(@sampage)中使用Pop的圓圈例子就是這樣。因爲strokeEnd和strokeStart不是默認屬性的一部分,因此你須要建立本身的自定義屬性(不過我可能錯失了什麼),以下:
[POPAnimatableProperty propertyWithName:@"strokeStart" initializer:^(POPMutableAnimatableProperty *prop) { prop.readBlock = ^(id obj, CGFloat values[]) { values[0] = [obj strokeStart]; }; prop.writeBlock = ^(id obj, const CGFloat values[]) { [obj setStrokeStart:values[0]]; }; }];
不得不說這個很強大,正如我以前所說的:表現層和模型層是同步的。
Pop上手體驗(ii)
Facebook Paper Tech Talk中讓我困惑的是Brian Amerige(@brianamerige)的一些話,尤爲是他向咱們示範如何跨過手勢和動畫之間的間隙(@ minute 47:40)。直接從視頻中得到的信息:
基於手勢速度旋轉UIView:
- (void)rotate:(UIPanGestureRecognizer*)recognizer { CGPoint velocity = [recognizer velocityInView:self.view]; POPSpringAnimation *spring = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerRotation]; spring.velocity = [NSValue valueWithCGPoint:velocity]; [_outletView.layer pop_addAnimation:spring forKey:@"rotationAnimation"]; }
如今,當你開始上手體驗時一切變得很是有趣:
當位置、大小和"dynamics"一同做用的時候會發生怎樣的事情?
代碼:
- (void)rotate:(UIPanGestureRecognizer*)recognizer { CGPoint velocity = [recognizer velocityInView:self.view]; POPSpringAnimation *positionAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPosition]; positionAnimation.velocity = [NSValue valueWithCGPoint:velocity]; positionAnimation.dynamicsTension = 5; positionAnimation.dynamicsFriction = 5.0f; positionAnimation.springBounciness = 20.0f; [_outletView.layer pop_addAnimation:positionAnimation forKey:@"positionAnimation"]; POPSpringAnimation *sizeAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerSize]; sizeAnimation.velocity = [NSValue valueWithCGPoint:velocity]; sizeAnimation.springBounciness = 1.0f; sizeAnimation.dynamicsFriction = 1.0f; [_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很容易作出來。
- (void)hidePopup { _isMenuOpen = NO; POPBasicAnimation *opacityAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity]; opacityAnimation.fromValue = @(1); opacityAnimation.toValue = @(0); [_popUp.layer pop_addAnimation:opacityAnimation forKey:@"opacityAnimation"]; POPBasicAnimation *positionAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPosition]; positionAnimation.fromValue = [NSValue valueWithCGPoint:VisiblePosition]; positionAnimation.toValue = [NSValue valueWithCGPoint:HiddenPosition]; [_popUp.layer pop_addAnimation:positionAnimation forKey:@"positionAnimation"]; POPSpringAnimation *scaleAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; scaleAnimation.fromValue = [NSValue valueWithCGSize:CGSizeMake(1.0f, 1.0f)]; scaleAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(0.5f, 0.5f)]; [_popUp.layer pop_addAnimation:scaleAnimation forKey:@"scaleAnimation"]; }
展現:
- (void)showPopup { _isMenuOpen = YES; POPBasicAnimation *opacityAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity]; opacityAnimation.fromValue = @(0); opacityAnimation.toValue = @(1); opacityAnimation.beginTime = CACurrentMediaTime() + 0.1; [_popUp.layer pop_addAnimation:opacityAnimation forKey:@"opacityAnimation"]; POPBasicAnimation *positionAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPosition]; positionAnimation.fromValue = [NSValue valueWithCGPoint:VisibleReadyPosition]; positionAnimation.toValue = [NSValue valueWithCGPoint:VisiblePosition]; [_popUp.layer pop_addAnimation:positionAnimation forKey:@"positionAnimation"]; POPSpringAnimation *scaleAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; scaleAnimation.fromValue = [NSValue valueWithCGSize:CGSizeMake(0.5, 0.5f)]; scaleAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(1.0f, 1.0f)];//@(0.0f); scaleAnimation.springBounciness = 20.0f; scaleAnimation.springSpeed = 20.0f; [_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):
還真有用:
POPAnimatableProperty *constantProperty = [POPAnimatableProperty propertyWithName:@"constant" initializer:^(POPMutableAnimatableProperty *prop){ prop.readBlock = ^(NSLayoutConstraint *layoutConstraint, CGFloat values[]) { values[0] = [layoutConstraint constant]; }; prop.writeBlock = ^(NSLayoutConstraint *layoutConstraint, const CGFloat values[]) { [layoutConstraint setConstant:values[0]]; }; }]; POPSpringAnimation *constantAnimation = [POPSpringAnimation animation]; constantAnimation.property = constantProperty; constantAnimation.fromValue = @(_layoutConstraint.constant); constantAnimation.toValue = @(200); [_layoutConstraint pop_addAnimation:constantAnimation forKey:@"constantAnimation"];
感謝Jake Marsh (@jakemarsh).
這是一個小便籤,我沒有注意到kPOPLayoutConstraintConstant,因此你無需建立一個自定義POPAnimatableProperty。
Pop上手體驗 (v)
在上手體驗Pop幾天後,有一點就是除了享受它,我應該作一點貢獻。
在該系列的第一篇中,我爲strokeStart和strokeEnd (二者均屬於CAShapeLayer)建立了自定義屬性:
[POPAnimatableProperty propertyWithName:@"strokeStart" initializer:^(POPMutableAnimatableProperty *prop) { prop.readBlock = ^(id obj, CGFloat values[]) { values[0] = [obj strokeStart]; }; prop.writeBlock = ^(id obj, const CGFloat values[]) { [obj setStrokeStart:values[0]]; }; }];
這個過程有點工做量,但不用懼怕。個人第一個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是如何實現的:
{kPOPViewBackgroundColor, ^(UIView *obj, CGFloat values[]) { POPUIColorGetRGBAComponents(obj.backgroundColor, values); }, ^(UIView *obj, const CGFloat values[]) { obj.backgroundColor = POPUIColorRGBACreate(values); }, 1.0 },
這個例子使用了POPUIColorGetRGBAComponents和POPUIColorRGBACreate:
void POPUIColorGetRGBAComponents(UIColor *color, CGFloat components[]) { return POPCGColorGetRGBAComponents(color.CGColor, components); } UIColor *POPUIColorRGBACreate(const CGFloat components[]) { CGColorRef colorRef = POPCGColorRGBACreate(components); UIColor *color = [[UIColor alloc] initWithCGColor:colorRef]; CGColorRelease(colorRef); return color; }
這個輔助方法位於POPCGUtils上,雖然POPLayerExtras上有不少。做爲一個「良好公民」,你能夠建立其餘方法,因此用戶可把它們用於其餘類似的屬性行爲。
1. 爲test suit添加你的屬性!因爲那些屬性闇昧不明,因此我僅把它添加到了POPAnimatablePropertyTests.m的testProvidedExistence,以確保它的實現是確實存在的。
2. 若是你作了不同凡響的事情,而且沒有覆蓋默認的test suit,那麼你須要更多的測試。
隨着個人需求的增加,我將會爲Pop貢獻更多。
原文:
推薦閱讀: