测试已测试 | ✓ |
语言语言 | Obj-CObjective C |
许可协议 | MIT |
发布上次发布 | 2017年10月 |
SwiftSwift 版本 | 4.0 |
由 Jeff Hurray, Jeff Hurray 维护。
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
CAAnimations和UIView动画非常强大,但很难将多个动画连接在一起,尤其是在改变锚点时。
此外,复杂的动画难以阅读。
比如说,我想将myView向右移动50像素,具有弹簧效果,然后当移动完成后,将背景颜色更改为了简化效果
[UIView animateWithDuration:1.0
delay:0.0
usingSpringWithDamping:0.8
initialSpringVelocity:1.0
options:0 animations:^{
CGPoint newPosition = self.myView.frame.origin;
newPosition.x += 50;
self.myView.frame.origin = newPosition;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
self.myView.backgroundColor = [UIColor purpleColor];
} completion:nil];
}];
这太糟糕了... 使用JHChainableAnimations只需要一行代码。
JHChainableAnimator *animator = [[JHChainableAnimator alloc] initWithView:self.myView];
animator.moveX(50).spring.thenAfter(1.0).makeBackground([UIColor purpleColor]).easeIn.animate(0.5);
当然也有很多好的动画库,比如RBBAnimation、DCAnimationKit和PMTween,但它们仍然无法提供强大的链式动画以及易于阅读/编写的语法。
您可以将此框架添加到项目的几种方式中。Objective-C框架名为JHChainableAnimations
,Swift框架名为ChainableAnimations
。有关Swift使用的更多信息,请参阅此处
pod 'JHChainableAnimations', '~> 3.0.1'
然后添加以下内容
#import <JHChainableAnimations/JHChainableAnimations.h>
pod 'ChainableAnimations', '~> 3.0.1'
然后添加以下内容
import ChainableAnimations
将JHChainableAnimations
框架添加到项目中。
将ChainableAnimations
框架添加到项目中。
您可以克隆仓库并将JHChainableAnimations中的文件手动添加。
要创建一个JHChainableAnimator
实例,您必须调用initWithView:
方法。
JHChainableAnimator *animator = [[JHChainableAnimator alloc] initWithView:self.myView];
链式属性(如moveX(x)
)必须放在视图和animate(t)
函数之间
以下是如何在一秒内将对象的大小翻倍的一个例子。
animator.makeScale(2.0).animate(1.0);
如果您想在缩放视图的同时移动视图,请添加另一个链式属性。顺序并不重要
animator.makeScale(2.0).moveXY(100, 50).animate(1.0);
// the same as animator.moveXY(100, 50).makeScale(2.0).animate(1.0);
链式属性的完整列表可以在此处找到
要链接动画,请使用thenAfter(t)
函数分隔链。
以下是一个如何对物体进行放大0.5秒然后移动1秒的例子。
animator.makeScale(2.0).thenAfter(0.5).moveXY(100, 50).animate(1.0);
要添加动画效果,在要应用该动画的链式属性之后调用效果方法。
以下是一个使用弹簧效果的放大视图的例子。
animator.makeScale(2.0).spring.animate(1.0);
如果您将2添加到相同的链式属性,第二个将取消第一个。
animator.makeScale(2.0).bounce.spring.animate(1.0);
// The same as animator.makeScale(2.0).spring.animate(1.0);
完整的动画效果属性列表可以在这里找到
要在动画链中的某个点进行视图锚定,请调用锚定方法。就像效果一样,在相同链中一个接一个地调用将取消第一个。
以下是一个围绕不同锚点旋转视图的例子
animator.rotateZ(180).anchorTopLeft.thenAfter(1.0).rotateZ(90).anchorCenter.animate(1.0);
// animator.rotateZ(90).anchorTopLeft.anchorCenter == animator.rotateZ(90).anchorCenter
完整的锚点属性列表可以在这里找到
要延迟动画,请调用wait(t)
或delay(t)
链式属性。
以下是在延迟0.5秒后移动视图的例子
animator.moveXY(100, 50).wait(0.5).animate(1.0);
// The same as animator.moveXY(100, 50).delay(0.5).animate(1.0);
要在动画完成后运行代码,请设置动画器的completionBlock
属性或调用animateWithCompletion(t, completion)*
函数。
animator.makeX(0).animateWithCompletion(1.0, ^{
NSLog(@"Animation Done");
});
等同于
animator.completionBlock = ^{
NSLog(@"Animation Done");
};
animator.makeX(0).animate(1.0);
可以通过将thenAfter(time)
方法替换为repeat(time, count)
方法来重复动画。这将重复先前定义的动画。
// The animator will double its scale 3 times for 0.5 seconds each before it calls `moveXY` and finishes the animation
animator.makeScale(2.0).repeat(0.5, 3).moveXY(100, 50).animate(1.0);
可以通过调用animateWithRepeat(time, count)
来重复动画的最后一部分。
// The animator will double its scale then rotate by 90 degrees 3 times for 1 second each.
animator.makeScale(2.0).thenAfter(0.5).rotate(90). animateWithRepeat(1.0, 3);
要暂停动画,请在动画器上调用pause
方法。当你调用暂停时,链中的当前动画将完成,但之后将不会有其他动作执行。您可以使用isPaused
和isAnimating
只读属性来检查状态。如果动画已暂停但未停止,它仍然会被视为animating
。
要在一个暂停的状态下继续,请在动画器上调用resume
方法。
要停止动画并清除状态,请在动画器上调用stop
方法。
// In this case the `moveX` animation will execute but the `moveY` will not
// If `resume` is called `moveY` will be executed
// If `stop` is called, nothing will be executed and the animator will get a fresh state
animator.moveX(10).thenAfter(0.5).moveY(10).animate(0.5);
[animator pause];
您可以通过调用preAnimationBlock(block)
、animationBlock(block)
和postAnimationBlock(block)
方法来挂钩到动画过程的各个步骤。所有这些方法都接受一个简单的块void(^)()
作为参数。在动画链中调用这些方法的顺序不影响。
animator.moveX(10).preAnimationBlock(^{
NSLog(@"before the first animation");
}).thenAfter(1.0).postAnimationBlock(^{
NSLog(@"After the second animation");
}).moveY(10).animate(1.0);
您还可以在UIBezierPath上动画视图。创建一个UIBezierPath *
实例,然后向其中添加点或曲线或线条,并在可链属性中使用它。
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:self.myView.center];
[path addLineToPoint:CGPointMake(25, 400)];
[path addLineToPoint:CGPointMake(300, 500)];
animator.moveOnPath(path).animate(1.0);
动画效果不适用于路径移动。
使用transform
链式属性。这对于受自动布局约束的视图来说更好。您不应将这些属性与其他可链属性混合使用。
animatorForViewWithConstraints.transformX(50).transformScale(2).animate(1.0);
现在使用版本2.x
的JHChainableAnimations与Swift的结合要容易阅读一些。我创建了一个用于Swift的单独框架,它提供了一个名为ChainableAnimator
的类。这是一个对JHChainableAnimator
的瘦包装器,它具有更易于阅读的语法。
let animator = ChainableAniamtor(view: myView)
animator.moveX(x: 50).thenAfter(t: 1.0).rotate(angle: 360).bounce.animate(t:1.0)
所有Objective-C方法都映射到Swift方法。
属性 | 接受... | 使用方法 |
---|---|---|
- (JHChainableRect) makeFrame; | CGRect | animator.makeFrame(rect).animate(1.0); |
- (JHChainableRect) makeBounds; | CGRect | animator.makeBounds(rect).animate(1.0); |
- (JHChainableSize) makeSize; | (CGFloat: width, CGFloat: height) | animator.makeSize(10, 20).animate(1.0); |
- (JHChainablePoint) makeOrigin; | (CGFloat: x, CGFloat: y) | animator.makeOrigin(10, 20).animate(1.0); |
- (JHChainablePoint) makeCenter; | (CGFloat: x, CGFloat: y) | animator.makeCenter(10, 20).animate(1.0); |
- (JHChainableFloat) makeX; | (CGFloat: f) | animator.makeX(10).animate(1.0); |
- (JHChainableFloat) makeY; | (CGFloat: f) | animator.makeY(10).animate(1.0); |
- (JHChainableFloat) makeWidth; | (CGFloat: f) | animator.makeWidth(10).animate(1.0); |
- (JHChainableFloat) makeHeight; | (CGFloat: f) | animator.makeHeight(10).animate(1.0); |
- (JHChainableFloat) makeOpacity; | (CGFloat: f) | animator.makeOpacity(10).animate(1.0); |
- (JHChainableColor) makeBackground; | (UIColor: 颜色) | animator.makeBackground(color).animate(1.0); |
- (JHChainableColor) makeBorderColor; | (UIColor: 颜色) | animator.makeBorderColor(color).animate(1.0); |
- (JHChainableFloat) makeBorderWidth; | (CGFloat: f) | animator.makeBorderWidth(3.0).animate(1.0); |
- (JHChainableFloat) makeCornerRadius; | (CGFloat: f) | animator.makeCornerRadius(3.0).animate(1.0); |
- (JHChainableFloat) makeScale; | (CGFloat: f) | animator.makeScale(2.0).animate(1.0); |
- (JHChainableFloat) makeScaleX; | (CGFloat: f) | animator.makeScaleX(2.0).animate(1.0); |
- (JHChainableFloat) makeScaleY; | (CGFloat: f) | animator.makeScaleY(2.0).animate(1.0); |
- (JHChainablePoint) makeAnchor; | (CGFloat: x, CGFloat: y) | animator.makeAnchor(0.5, 0.5).animate(1.0); |
- (JHChainableFloat) moveX; | (CGFloat: f) | animator.moveX(50).animate(1.0) |
- (JHChainableFloat) moveY; | (CGFloat: f) | animator.moveY(50).animate(1.0) |
- (JHChainablePoint) moveXY; | (CGFloat: x, CGFloat: y) | animator.moveXY(100, 50).animate(1.0) |
- (JHChainableFloat) moveHeight; | (CGFloat: f) | animator.moveHeight(50).animate(1.0) |
- (JHChainableFloat) moveWidth; | (CGFloat: f) | animator.moveWidth(50).animate(1.0) |
- (JHChainableDegrees) rotateX; | (CGFloat: angle) #不是角度! | animator.rotateX(360).animate(1.0); |
- (JHChainableDegrees) rotateY; | (CGFloat: angle) #不是角度! | animator.rotateY(360).animate(1.0); |
- (JHChainableDegrees) rotateZ; | (CGFloat: angle) #不是角度! | animator.rotateZ(360).animate(1.0); |
- (JHChainablePolarCoordinate) movePolar; | (CGFloat: radius, CGFloat: angle) | animator.movePolar(30, 90).animate(1.0); |
- (JHChainableBezierPath) moveOnPath; | (UIBezierPath *path) | animator.moveOnPath(path).animate(1.0); |
- (JHChainableBezierPath) moveAndRotateOnPath; | (UIBezierPath *path) | animator.moveAndRotateOnPath(path).animate(1.0); |
- (JHChainableBezierPath) moveAndReverseRotateOnPath; | (UIBezierPath *path) | animator.moveAndReverseRotateOnPath(path).animate(1.0); |
- (JHChainableFloat) transformX; | (CGFloat f) | animator.transformX(50).animate(1.0); |
- (JHChainableFloat) transformX; | (CGFloat f) | animator.transformX(50).animate(1.0); |
- (JHChainableFloat) transformY; | (CGFloat f) | animator.transformY(50).animate(1.0); |
- (JHChainableFloat) transformZ; | (CGFloat f) | animator.transformZ(50).animate(1.0); |
- (JHChainablePoint) transformXY; | (CGFloat x, CGFloat y) | animator.transformXY(50, 100).animate(1.0); |
- (JHChainableFloat) transformScale; | (CGFloat f) | animator.transformScale(50).animate(1.0); |
- (JHChainableFloat) transformScaleX; | (CGFloat f) | animator.transformScaleX(50).animate(1.0); |
- (JHChainableFloat) transformScaleY; | (CGFloat f) | animator.transformScaleY(50).animate(1.0); |
- (JHChainableAnimator *) transformIdentity; | 无 | animator.transformIdentity.animate(1.0); |
有关这些函数的快速查阅可在此处找到here
这些动画函数来自一个很酷的键帧动画库,可在此处找到
它们基于可在此处找到的jQuery easing函数
有关锚定的信息可在此处找到here
我已经收到了很多关于接下来做什么的极好的建议。如果您认为这里缺少任何东西,请告诉我!以下是我计划按照顺序工作的内容。
请随意通过[email protected]给我发邮件。我很乐意听到您的想法,或者看到已使用此技术的示例。