iOS開發之Pop框架(三)

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。如果它不止有一個值,那麼它可能會像kPOPXY。然後在POPAnimatableProperty.m上添加實際值,它可能會是NSString * const kPop = @""。如果你不確定如何命名,可以看看其他屬性。
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,那麼你需要更多的測試。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章