UI定制 - 水波纹效果(中心向外扩散)

水波纹(中心向外扩散)

1 - 代码封装

// - RippleView.h 

复制代码
 1 #import <UIKit/UIKit.h>
 2 @interface RippleView : UIView
 3 
 4 @property (nonatomic, assign) NSTimeInterval timeInterval;// 波浪轮推间隔
 5 @property (nonatomic, assign) NSTimeInterval duration;    // 动画完成时间
 6 @property (nonatomic, assign) NSInteger waveCount;// 波浪圈数
 7 @property (nonatomic, assign) CGFloat minRadius;  // 半径
 8 
 9 - (void)startAnimating;// 开启
10 - (void)stopAnimating; // 关闭
11 
12 @end
复制代码

// - RippleView.m

复制代码
  1 #import "RippleView.h"
  2 @interface RippleView ()
  3 
  4 @property (nonatomic, assign) BOOL animating;
  5 @property (nonatomic, strong) NSMutableArray<CAShapeLayer *> *shapeLayers;
  6 
  7 @end
  8 
  9 @implementation RippleView
 10 
 11 // 默认状态
 12 - (instancetype)init{
 13     if (self = [super init]) {
 14         self.tintColor = [UIColor greenColor];
 15         self.timeInterval = .5;
 16         self.duration = 4;
 17         self.waveCount = 8;
 18         self.minRadius = .2;
 19         self.shapeLayers = [NSMutableArray array];
 20     }
 21     return self;
 22 }
 23 
 24 - (void)layoutSubviews{
 25     [super layoutSubviews];
 26     [self _reloadShapeLayers];
 27 }
 28 
 29 
 30 - (CAShapeLayer *)newShapeLayer{
 31 
 32     CGPoint center = CGPointMake(CGRectGetWidth([self bounds]) / 2., CGRectGetHeight([self bounds]) / 2);
 33     UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, [self minRadius] * 2, [self minRadius] * 2) cornerRadius:[self minRadius]];
 34 
 35     CAShapeLayer *shapeLayer = [CAShapeLayer layer];
 36     shapeLayer.frame = CGRectMake(center.x - [self minRadius], center.y - [self minRadius], [self minRadius] * 2, [self minRadius] * 2);
 37     shapeLayer.opacity = 0;
 38     shapeLayer.lineWidth = 0.f;
 39     shapeLayer.position = center;
 40     shapeLayer.path = [path CGPath];
 41     shapeLayer.anchorPoint =  CGPointMake(0.5, 0.5);
 42     shapeLayer.fillColor = [[self tintColor] CGColor];
 43     shapeLayer.strokeColor = [[UIColor clearColor] CGColor];
 44 
 45     return shapeLayer;
 46 }
 47 
 48 - (CAAnimation *)newWaveAnimation{
 49 
 50     CGFloat maxRadius = MAX([self minRadius], MIN(CGRectGetWidth([self bounds]) / 2., CGRectGetHeight([self bounds]) / 2.));
 51     CGFloat scale = maxRadius / [self minRadius];
 52 
 53     CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
 54     scaleAnimation.removedOnCompletion = NO;
 55     scaleAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
 56     scaleAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(scale, scale, 1)];
 57 
 58     CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
 59     alphaAnimation.removedOnCompletion= NO;
 60     alphaAnimation.fromValue = @1;
 61     alphaAnimation.toValue = @0;
 62 
 63     CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
 64     animationGroup.removedOnCompletion = NO;
 65     animationGroup.animations = @[scaleAnimation, alphaAnimation];
 66     animationGroup.duration = [self duration];
 67     animationGroup.repeatCount = NSIntegerMax;
 68     animationGroup.removedOnCompletion = YES;
 69 
 70     return animationGroup;
 71 }
 72 
 73 - (void)_reloadShapeLayers{
 74 
 75     for (CAShapeLayer *shapeLayer in [self shapeLayers]) {
 76         [shapeLayer removeAllAnimations];
 77         [shapeLayer removeFromSuperlayer];
 78     }
 79 
 80     [[self shapeLayers] removeAllObjects];
 81 
 82     for (NSInteger index = 0; index < [self waveCount]; index++) {
 83         CAShapeLayer *shapeLayer = [self newShapeLayer];
 84         [[self layer] addSublayer:shapeLayer];
 85         [[self shapeLayers] addObject:shapeLayer];
 86     }
 87 
 88     if ([self animating]) {
 89         [self startAnimating];
 90     } else {
 91         [self stopAnimating];
 92     }
 93 }
 94 
 95 - (void)_appendAnimationParameters:(NSArray *)parameters{
 96     CALayer *layer = [parameters firstObject];
 97     CAAnimation *animation = [parameters lastObject];
 98 
 99     if ([self animating]) {
100         [layer addAnimation:animation forKey:nil];
101     }
102 }
103 
104 // 开启动画
105 - (void)startAnimating{
106     [self stopAnimating];
107 
108     self.animating = YES;
109 
110     [[self shapeLayers] enumerateObjectsUsingBlock:^(CAShapeLayer * shapeLayer, NSUInteger index, BOOL *stop) {
111         CAAnimation *animation = [self newWaveAnimation];
112         animation.removedOnCompletion = NO;
113         [self performSelector:@selector(_appendAnimationParameters:) withObject:@[shapeLayer, animation] afterDelay:index * [self timeInterval]];
114     }];
115 }
116 
117 // 关闭动画
118 - (void)stopAnimating{
119     self.animating = NO;
120 
121     for (CAShapeLayer *shapeLayer in [self shapeLayers]) {
122         [shapeLayer removeAllAnimations];
123     }
124 }
125 
126 @end
复制代码

2 - 如何使用 

复制代码
 1 #import "ViewController.h"
 2 #import "RippleView.h"
 3 
 4 @implementation ViewController
 5 
 6 - (void)viewDidLoad {
 7     self.view.backgroundColor = [UIColor cyanColor];
 8     
 9     // 初始化
10     RippleView *test = [[RippleView alloc] init];
11     test.frame = CGRectMake(100, 200, self.view.frame.size.width - 200, 120);
12     [self.view addSubview:test];
13     [test startAnimating];
14 
15     // 自定制
16     test.tintColor = [UIColor redColor];
17     test.duration = 4;  // 动画时间
18     test.minRadius = .2;// 源点:初始波浪最小半径
19     test.timeInterval = 1;// 每个波浪间隔 1 秒后执行
20     test.waveCount = 4;   // 4 个波浪
21 }
22 
23 @end
复制代码

运行效果

  

posted on   低头捡石頭  阅读(108)  评论(0编辑  收藏  举报

编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示