点击View后的动画:如何在Swift中实现此类涟漪效果?
Hey there! 很高兴帮你解决Swift里点击View的涟漪效果实现问题,我给你整理了几种实用的方案,从最简单的原生实现到自定义动画都有,你可以根据需求选~
如果你的View可以替换成UIButton,那这绝对是最省心的方案——UIButton在iOS 10+自带了触摸涟漪效果,完全不用自己写动画逻辑,几行代码就能搞定:
let rippleButton = UIButton(type: .system) rippleButton.frame = CGRect(x: 50, y: 50, width: 200, height: 50) rippleButton.setTitle("点击我", for: .normal) // 自定义涟漪颜色(可选,默认用系统tint色) rippleButton.tintColor = .systemBlue rippleButton.backgroundColor = .systemGray5 view.addSubview(rippleButton)
点击这个Button时,会自动从触摸点扩散出半透明的涟漪,你还能通过tintColor调整涟漪的颜色,完美适配大部分场景。
方法2:给普通UIView自定义涟漪动画
如果必须用UIView(比如一些自定义的容器View),我们可以通过CAShapeLayer配合核心动画来实现点击扩散的效果。我平时做项目的时候会把这个逻辑封装成UIView的扩展,方便复用:
extension UIView { func addRippleEffect(at touchPoint: CGPoint) { // 1. 创建初始大小为0的圆形Layer let rippleLayer = CAShapeLayer() let startPath = UIBezierPath(ovalIn: CGRect(origin: touchPoint, size: .zero)) rippleLayer.path = startPath.cgPath rippleLayer.fillColor = UIColor.systemBlue.withAlphaComponent(0.3).cgColor self.layer.addSublayer(rippleLayer) // 2. 计算最终的圆形大小(要覆盖整个View) let maxSide = max(self.bounds.width, self.bounds.height) let endFrame = CGRect( x: touchPoint.x - maxSide/2, y: touchPoint.y - maxSide/2, width: maxSide, height: maxSide ) let endPath = UIBezierPath(ovalIn: endFrame).cgPath // 3. 组合扩散动画+透明度消失动画 let pathAnim = CABasicAnimation(keyPath: "path") pathAnim.fromValue = startPath.cgPath pathAnim.toValue = endPath pathAnim.duration = 0.6 let opacityAnim = CABasicAnimation(keyPath: "opacity") opacityAnim.fromValue = 0.3 opacityAnim.toValue = 0 opacityAnim.duration = 0.6 let groupAnim = CAAnimationGroup() groupAnim.animations = [pathAnim, opacityAnim] groupAnim.timingFunction = CAMediaTimingFunction(name: .easeOut) groupAnim.fillMode = .forwards groupAnim.isRemovedOnCompletion = false rippleLayer.add(groupAnim, forKey: "rippleEffect") // 4. 动画结束后移除Layer,避免内存泄漏 DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) { rippleLayer.removeFromSuperlayer() } } }
然后给你的UIView添加点击手势,调用这个方法就行:
let customView = UIView(frame: CGRect(x: 50, y: 150, width: 200, height: 50)) customView.backgroundColor = .systemGray5 customView.isUserInteractionEnabled = true view.addSubview(customView) // 添加点击手势 let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleViewTap(_:))) customView.addGestureRecognizer(tapGesture) // 处理点击事件 @objc private func handleViewTap(_ gesture: UITapGestureRecognizer) { guard let tappedView = gesture.view else { return } let touchPoint = gesture.location(in: tappedView) tappedView.addRippleEffect(at: touchPoint) }
这个方案的好处是完全自定义,你可以调整动画时长、涟漪颜色、缓动效果,适配任何UIView。
方法3:SwiftUI中的涟漪效果实现
如果是用SwiftUI开发,那就更简单了,不管是用Button还是普通View都能轻松实现:
系统原生方案(Button)
SwiftUI的Button默认就带涟漪效果,只需要简单设置:
struct RippleDemoView: View { var body: some View { Button(action: { // 点击后的逻辑 }) { Text("点击我") .frame(width: 200, height: 50) .background(Color.systemGray5) } .tint(.systemBlue) // 调整涟漪颜色 .buttonStyle(.plain) // 或者用.automatic,默认就有涟漪 } }
自定义普通View的涟漪效果
如果要给非Button的View加涟漪,用TapGesture配合状态动画就行:
struct CustomRippleSwiftUIView: View { @State private var ripplePos: CGPoint = .zero @State private var rippleScale: CGFloat = 0 @State private var rippleOpacity: Double = 0.3 var body: some View { ZStack { Color.systemGray5 .frame(width: 200, height: 50) .onTapGesture { location in // 重置涟漪状态 ripplePos = location rippleScale = 0 rippleOpacity = 0.3 // 执行扩散+消失动画 withAnimation(.easeOut(duration: 0.6)) { rippleScale = max(200, 50) * 2 // 确保覆盖整个View rippleOpacity = 0 } } // 涟漪视图 Circle() .fill(Color.systemBlue.opacity(rippleOpacity)) .frame(width: rippleScale, height: rippleScale) .position(ripplePos) } } }
小提示
- 自定义动画时,记得在动画结束后清理资源(比如UIKit里移除Layer),避免内存泄漏
- 可以调整
duration和timingFunction来改变涟漪的快慢和弹性效果 - 涟漪颜色建议用主色调的半透明色,这样既不突兀又能和App风格统一
内容的提问来源于stack exchange,提问作者Harshal Pathak
相关产品推荐
相关产品推荐

