iOS视图的Material Design水波纹效果实现(附实现代码)

效果图

实现思路

在开始编写任何代码之前,我们需要决定如何实现这个效果。它会是某个特定UIView的子类吗?还是一种新的UIView类型,可以作为子视图添加?我选择了以上都不是,而是决定使用扩展(Category)来扩展UIView,这是一个简单而有效的方法。通过创建一个新的扩展,我们可以无缝地将这个效果添加到任何UIView中,而不会遇到任何麻烦。

既然我们知道了要在哪里编写代码,我们需要为这个动画定义一个API。目前,这个实现将是简单的。对于动画,我们需要定义几个要素:

  • 作为动画起点的位置。
  • 我们想要的涟漪颜色。
  • 我们希望涟漪有多大。
  • 我们希望它扩展的速度,或者换句话说,我们希望动画持续多长时间。
  • 涟漪何时开始淡出。

在本文的末尾,你可以找到一个包含所有这些代码的存储库链接,但让我们先看看如何构建代码。

我们的UIView类别(Category)将如下所示:

objectivec 复制代码
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

typedef void(^JCNFlashButtonDidClickBlock)(void);

@interface UIView (JCNSimpleRipple)

/// clickCallback
@property (nonatomic, strong) JCNFlashButtonDidClickBlock clickCallback;

///  开始波纹动画
/// - Parameters:
///   - origin: starting point for our animation
///   - color: The color we want our ripple to have.
///   - duration: how long we want our animation to last.
///   - radius: How big we want our ripple to be.
///   - fadeAfter: When the ripple should start fading.
///   - callback: 动画结束后的回调

- (void)rippleStartingAt:(CGPoint)origin
               withColor:(UIColor *)color
                duration:(NSTimeInterval)duration
                  radius:(CGFloat)radius
               fadeAfter:(NSTimeInterval)fadeAfter
            clickCallbck:(JCNFlashButtonDidClickBlock)callback;

@end

NS_ASSUME_NONNULL_END

我们的动画将在一个新的CAShapeLayer中进行。这个新的图层将位于所有其他图层的后面,以确保它不会遮挡UIView的任何内容。然后,我们将添加一个CABasicAnimation来对路径进行变换。这个路径将是实际的涟漪,它将从一个小小的圆形变换为我们在参数中指定的大小。

这是一个简化版的实现方法:

ini 复制代码
    CAShapeLayer *rippleLayer = [CAShapeLayer layer];
    UIBezierPath *startPath = [UIBezierPath bezierPathWithArcCenter:origin
                                                         radius:startRadius
                                                         startAngle:0 endAngle:FULL
                                                         clockwise:YES];

    UIBezierPath *endPath = [UIBezierPath bezierPathWithArcCenter:origin
                                                           radius:endRadius
                                                       startAngle:0 endAngle:FULL
                                                        clockwise:YES];

    CABasicAnimation *rippleAnimation = [CABasicAnimation animationWithKeyPath:@"path"];

    rippleAnimation.fillMode = kCAFillModeBoth;
    rippleAnimation.duration = duration;
    rippleAnimation.fromValue = (id)(startPath.CGPath);
    rippleAnimation.toValue = (id)(endPath.CGPath);
    rippleAnimation.removedOnCompletion = NO;
    rippleAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];

    [self.layer insertSublayer:rippleLayer atIndex:0];
    [rippleLayer addAnimation:rippleAnimation forKey:nil];

对于淡出效果,我们将以类似的方式处理:

ini 复制代码
CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeOutAnimation.fromValue = @1;
fadeOutAnimation.toValue = @0;
fadeOutAnimation.duration = fadeOutDuration;
fadeOutAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];

[rippleLayer addAnimation:fadeOutAnimation forKey:nil];

最后,一旦我们完成动画,我们可以从UIView中移除涟漪动画。但是有一个小问题。通常我们会使用CAAnimationDelegate并实现适当的回调方法,但是我们要么必须存储对新添加图层的引用,要么必须遍历子图层并找到相应的图层。这也意味着我们不能(至少不容易)同时拥有多个涟漪动画。因此,我们将采用非常简单的解决方案,在动画完成时,我们将排队执行一段代码块,并在该代码块中将涟漪图层移除。

objc 复制代码
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, duration * NSEC_PER_SEC), dispatch_get_main_queue(), ^{

    [rippleLayer removeAllAnimations]; 
    [rippleLayer removeFromSuperlayer];

});

仓库链接: github.com/Yinjianhua4...

相关推荐
2501_9159214315 分钟前
iOS 应用代上架流程,多工具组合与使用 开心上架 跨平台自动化上传指南
android·ios·小程序·uni-app·自动化·cocoa·iphone
日日行不惧千万里20 分钟前
2025最新仿默往 IM 即时通讯系统源码(PC + Web + iOS + Android)完整版发布!
android·ios
歪歪10020 分钟前
React Native开发Android&IOS流程完整指南
android·开发语言·前端·react native·ios·前端框架
全栈小51 小时前
【代码管理】在本地使用github和gitee之后,可能存在冲突,导致再次提交代码时提示Couldn‘t connect to server
gitee·github·代码管理工具
NocoBase1 小时前
11 个在 GitHub 上最受欢迎的开源无代码 AI 工具
低代码·ai·开源·github·无代码·ai agent·airtable·内部工具·app builder
阿里超级工程师2 小时前
ios云打包证书申请不需要苹果电脑也是可以的
ios·证书·云打包
今禾3 小时前
Git完全指南(中篇):GitHub团队协作实战
前端·git·github
whysqwhw3 小时前
KuiklyUI利用Kotlin Lambda函数实现声明式UI系统的深入分析
github
whysqwhw3 小时前
Kotlin扩展函数和带接收者的 Lambda 表达式实现DSL
github
天一生水water3 小时前
Failed to connect to github.com port 443
github