CADisplayLink是什麼
根據Apple的doc:
A CADisplayLink object is a timer object that allows your application to synchronize its drawing to the refresh rate of the display.
比起NSTimer,CADisplayLink可以確保系統渲染每一幀的時候我們的方法都被調用,從而保證了動畫的流暢性。
Demo
我們希望在animate一個view的時候給它加上果凍效果:
我們會把所有的邏輯都封裝到一個BlockView裡,在這個view裡首先申明一個property:
@property (strong, nonatomic) CADisplayLink *displayLink;
在動畫開始的時候,初始化displayLink,指定tick方法:
- (void)startAnimation { if (self.displayLink == nil) { self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)]; [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; } }
動畫結束的時候invalidate displayLink:
- (void)completeAnimation { [self.displayLink invalidate]; self.displayLink = nil; }
每個tick中,我們需要根據當前的位置重繪邊緣,所以只需調用setNeedsDisplay即可:
- (void)tick:(CADisplayLink *)displayLink { [self setNeedsDisplay]; }
在drawRect中,我們計算當前動畫的progress,然後進行繪制。需要注意的是,我們需要通過self.layer.presentationLayer來獲取動畫過程中的位置信息。
- (void)drawRect:(CGRect)rect { CALayer *layer = self.layer.presentationLayer; CGFloat 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)); 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]; }
最後,我們只需要這樣animate這個BlockView:
[self.blockView startAnimationFrom:from to:to]; [UIView animateWithDuration:1 delay:0 usingSpringWithDamping:0.85 initialSpringVelocity:0 options:0 animations:^{ self.blockView.center = CGPointMake(self.blockView.center.x, to); } completion:^(BOOL finished) { [self.blockView completeAnimation]; }];
Demo Project