1. 程式人生 > >Pop上手體驗(i-v)

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)  基於手勢速度旋轉UIView:
  1. - (void)rotate:(UIPanGestureRecognizer*)recognizer 
  2.     CGPoint velocity = [recognizer velocityInView:self.view]; 
  3.     POPSpringAnimation *spring = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerRotation]; 
  4.     spring.velocity = [NSValue valueWithCGPoint:velocity]; 
  5.     [_outletView.layer pop_addAnimation:spring forKey:@"rotationAnimation"]; 
現在,當你開始上手體驗時一切變得非常有趣: 當位置、大小和"dynamics"一同作用的時候會發生怎樣的事情? 程式碼:
  1. - (void)rotate:(UIPanGestureRecognizer*)recognizer 
  2.     CGPoint velocity = [recognizer velocityInView:self.view]; 
  3. POPSpringAnimation *positionAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPosition];   
  4. positionAnimation.velocity = [NSValue valueWithCGPoint:velocity];   
  5. positionAnimation.dynamicsTension = 5;   
  6. positionAnimation.dynamicsFriction = 5.0f;   
  7. positionAnimation.springBounciness = 20.0f;   
  8. [_outletView.layer pop_addAnimation:positionAnimation forKey:@"positionAnimation"]; 
  9. POPSpringAnimation *sizeAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerSize];   
  10. sizeAnimation.velocity = [NSValue valueWithCGPoint:velocity];   
  11. sizeAnimation.springBounciness = 1.0f;   
  12. sizeAnimation.dynamicsFriction = 1.0f;   
  13. [_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.     POPBasicAnimation *positionAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPosition]; 
  8.     positionAnimation.fromValue = [NSValue valueWithCGPoint:VisiblePosition]; 
  9.     positionAnimation.toValue = [NSValue valueWithCGPoint:HiddenPosition]; 
  10.     [_popUp.layer pop_addAnimation:positionAnimation forKey:@"positionAnimation"]; 
  11.     POPSpringAnimation *scaleAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 
  12.     scaleAnimation.fromValue  = [NSValue valueWithCGSize:CGSizeMake(1.0f, 1.0f)]; 
  13.     scaleAnimation.toValue  = [NSValue valueWithCGSize:CGSizeMake(0.5f, 0.5f)]; 
  14.     [_popUp.layer pop_addAnimation:scaleAnimation forKey:@"scaleAnimation"]; 
展示:
  1. - (void)showPopup 
  2.     _isMenuOpen = YES; 
  3.     POPBasicAnimation *opacityAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerOpacity]; 
  4.     opacityAnimation.fromValue = @(0); 
  5.     opacityAnimation.toValue = @(1); 
  6.     opacityAnimation.beginTime = CACurrentMediaTime() + 0.1; 
  7.     [_popUp.layer pop_addAnimation:opacityAnimation forKey:@"opacityAnimation"]; 
  8.     POPBasicAnimation *positionAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPosition]; 
  9.     positionAnimation.fromValue = [NSValue valueWithCGPoint:VisibleReadyPosition]; 
  10.     positionAnimation.toValue = [NSValue valueWithCGPoint:VisiblePosition]; 
  11.     [_popUp.layer pop_addAnimation:positionAnimation forKey:@"positionAnimation"]; 
  12.     POPSpringAnimation *scaleAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; 
  13.     scaleAnimation.fromValue  = [NSValue valueWithCGSize:CGSizeMake(0.5, 0.5f)]; 
  14.     scaleAnimation.toValue  = [NSValue valueWithCGSize:CGSizeMake(1.0f, 1.0f)];//@(0.0f);
  15.     scaleAnimation.springBounciness = 20.0f; 
  16.     scaleAnimation.springSpeed = 20.0f; 
  17.     [_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) 還真有用:
  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. POPSpringAnimation *constantAnimation = [POPSpringAnimation animation];   
  10. constantAnimation.property = constantProperty;   
  11. constantAnimation.fromValue = @(_layoutConstraint.constant);   
  12. constantAnimation.toValue = @(200);   
  13. [_layoutConstraint pop_addAnimation:constantAnimation forKey:@"constantAnimation"]; 
這是一個小便籤,我沒有注意到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. UIColor *POPUIColorRGBACreate(const CGFloat components[])   
  4.   CGColorRef colorRef = POPCGColorRGBACreate(components); 
  5.   UIColor *color = [[UIColor alloc] initWithCGColor:colorRef]; 
  6.   CGColorRelease(colorRef); 
  7.   return color; 
這個輔助方法位於POPCGUtils上,雖然POPLayerExtras上有很多。作為一個“良好公民”,你可以建立其他方法,所以使用者可把它們用於其他相似的屬性行為。 1. 為test suit新增你的屬性!由於那些屬性闇昧不明,所以我僅把它新增到了POPAnimatablePropertyTests.m的testProvidedExistence,以確保它的實現是確實存在的。 2. 如果你做了與眾不同的事情,並且沒有覆蓋預設的test suit,那麼你需要更多的測試。 隨著我的需求的增長,我將會為Pop貢獻更多。