效果图:
这里自定义一个View(图中的蓝色视图)
- .h文件:
#import <UIKit/UIKit.h>
@interface BlockView : UIView
- (void)startAnimationFrom:(CGFloat)from to:(CGFloat)to;
- (void)completeAnimation;
@end
声明了两个方法,一个用于开始动画,另外一个用于通知视图已完成动画
- m 文件:
#import "BlockView.h"
@interface BlockView ()
@property (strong, nonatomic) CADisplayLink *displayLink;
@property (nonatomic) CGFloat from;
@property (nonatomic) CGFloat to;
@property (nonatomic) BOOL animating;
@end
@implementation BlockView
- (void)startAnimationFrom:(CGFloat)from to:(CGFloat)to {
self.from = from;
self.to = to;
self.animating = YES;
if (self.displayLink == nil) {
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
}
}
- (void)completeAnimation {
self.animating = NO;
[self.displayLink invalidate];
self.displayLink = nil;
}
- (void)tick:(CADisplayLink *)displayLink {
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
CALayer *layer = self.layer.presentationLayer;
CGFloat progress = 1;
if (!self.animating) {
progress = 1;
} else {
progress = 1 - (layer.position.y - self.to) / (self.from - self.to);
}
CGFloat height = CGRectGetHeight(rect);
CGFloat deltaHeight = height / 2 * (0.5 - fabs(progress - 0.5));
NSLog(@"delta:%f", deltaHeight);
CGPoint topLeft = CGPointMake(0, deltaHeight);
CGPoint topRight = CGPointMake(CGRectGetWidth(rect), deltaHeight);
CGPoint bottomLeft = CGPointMake(0, height);
CGPoint bottomRight = CGPointMake(CGRectGetWidth(rect), height);
UIBezierPath* path = [UIBezierPath bezierPath];
[[UIColor blueColor] setFill];
[path moveToPoint:topLeft];
[path addQuadCurveToPoint:topRight controlPoint:CGPointMake(CGRectGetMidX(rect), 0)];
[path addLineToPoint:bottomRight];
[path addQuadCurveToPoint:bottomLeft controlPoint:CGPointMake(CGRectGetMidX(rect), height - deltaHeight)];
[path closePath];
[path fill];
}
@end
- 在动画开始的时候,初始化displayLink,指定tick方法;
- 动画结束的时候invalidate displayLink:
- 每一次调用tick时,我们需要根据当前的位置重绘边缘,所以只需调用setNeedsDisplay即可:
- 在drawRect中,我们计算当前动画的progress,然后进行绘制。
需要注意的是,我们需要通过self.layer.presentationLayer来获取动画过程中的位置信息。
另外,搭建视图时,我将自定义的View和控制器的View设置成了同样的背景色,而在绘制图层时填充了蓝色,这样才能看到果冻效果,如果忽略了背景色,可能会导致复制代码后运行起来看不出果冻效果,可以通过慢速动画发现,自定义View的上边缘会有黑色区域浮动.