如何为UIPageViewController的pageCurl类型CATransition添加弹性效果?
给PageCurl类型CATransition添加弹性效果的实现方案
嘿,这个需求很实用!原生的pageCurl类型CATransition确实没有直接提供弹性参数,但我们可以通过自定义动画进度曲线或者组合动画的方式来实现这个效果。下面分享两种经过验证的可行方案:
方案一:用CAKeyframeAnimation控制翻页进度实现弹性回弹
这个思路是通过关键帧动画来控制翻页的进度,让它先超过目标位置(比如你原来的0.2),再回弹到目标位置,模拟弹性效果。具体步骤如下:
- 先创建基础的
pageCurl过渡动画,但不直接添加到layer上 - 创建
CAKeyframeAnimation来驱动过渡动画的endProgress属性,设置弹性的关键帧值 - 将两个动画组合后添加到目标layer
代码示例:
// 1. 创建基础的pageCurl过渡动画 let pageCurlTransition = CATransition() pageCurlTransition.type = .pageCurl pageCurlTransition.subtype = .fromRight // 根据你的需求调整翻页方向 pageCurlTransition.duration = 0.5 // 总时长,包含弹性回弹部分 pageCurlTransition.fillMode = .forwards pageCurlTransition.isRemovedOnCompletion = false // 2. 创建关键帧动画控制endProgress,实现弹性效果 let progressAnimation = CAKeyframeAnimation(keyPath: "endProgress") progressAnimation.values = [0.0, 0.25, 0.2] // 先到0.25(过冲目标位置),再回弹到0.2 progressAnimation.keyTimes = [0.0, 0.7, 1.0] // 前70%时间完成过冲,后30%时间回弹 progressAnimation.timingFunctions = [ CAMediaTimingFunction(name: .easeInEaseOut), CAMediaTimingFunction(controlPoints: 0.2, 1.0, 0.3, 1.0) // 自定义弹性回弹曲线 ] // 3. 将进度动画绑定到过渡动画上 pageCurlTransition.add(progressAnimation, forKey: "elasticProgress") // 4. 添加到目标layer(比如UIPageViewController的view.layer) yourPageViewController.view.layer.add(pageCurlTransition, forKey: "elasticPageCurl")
关键细节解释:
values数组:定义了进度的变化路径,从0开始→超过目标的0.25→回到目标0.2,形成弹性回弹的视觉效果keyTimes:控制每个阶段的时间占比,你可以根据需求调整,比如想要更明显的弹性,就增加回弹阶段的时间占比- 自定义的
timingFunctions:第二个贝塞尔曲线用来模拟弹性的减速回弹,让动画更自然
方案二:组合两次CATransition动画(正向+反向)
如果觉得关键帧动画太复杂,也可以用两次过渡动画来模拟弹性:第一次翻到超过目标的位置,第二次快速回弹到目标位置。
代码示例:
let targetProgress: CGFloat = 0.2 let overshootProgress: CGFloat = 0.25 // 第一次动画:翻到过冲位置 let firstTransition = CATransition() firstTransition.type = .pageCurl firstTransition.subtype = .fromRight firstTransition.duration = 0.3 firstTransition.endProgress = overshootProgress firstTransition.fillMode = .forwards firstTransition.isRemovedOnCompletion = false // 第二次动画:回弹到目标位置 let secondTransition = CATransition() secondTransition.type = .pageCurl secondTransition.subtype = .fromLeft // 反向翻页实现回弹 secondTransition.duration = 0.2 secondTransition.endProgress = 1 - (overshootProgress - targetProgress) // 计算回弹的进度值 secondTransition.fillMode = .forwards secondTransition.isRemovedOnCompletion = false secondTransition.beginTime = CACurrentMediaTime() + 0.3 // 延迟到第一次动画结束后执行 // 依次添加动画到目标layer yourPageViewController.view.layer.add(firstTransition, forKey: "firstPageCurl") yourPageViewController.view.layer.add(secondTransition, forKey: "secondPageCurl")
注意事项:
- 第二次动画的
subtype要和第一次相反,才能实现回弹效果 beginTime要设置为第一次动画的时长,确保动画连贯不脱节- 可以调整两次动画的
duration和过冲的overshootProgress值,来控制弹性的强度
额外优化建议
- 如果需要更细腻的弹性效果,可以尝试用
UIView.animate(withDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:)包裹动画,但注意这个方法对CATransition的支持有限,可能需要结合layer动画灵活调整 - 多测试不同的
timingFunction参数,找到最符合你产品风格的弹性效果
内容的提问来源于stack exchange,提问作者TRG
相关产品推荐
相关产品推荐

