资讯详情

swiftui-expert-skill - animation-advanced

📅 2026/10/12 3:42:30 | 华诺云谱 👁 阅读
swiftui-expert-skill - animation-advanced
SwiftUI 进阶动画事务、阶段动画iOS 17、关键帧动画iOS 17、完成处理程序iOS 17以及Animatable宏iOS 26。目录事务阶段动画iOS 17关键帧动画iOS 17动画完成处理程序iOS 17Animatable 宏iOS 26事务SwiftUI 中所有动画的底层机制。基本用法// withAnimation 是 withTransaction 的简写withAnimation(.default){flag.toggle()}// 等价的显式事务vartransactionTransaction(animation:.default)withTransaction(transaction){flag.toggle()}.transaction 修饰符Rectangle().frame(width:flag?100:50,height:50).transaction{tint.animation.default}注意它的行为类似于已弃用的.animation(_:)不带 value 参数——它会在每次状态变化时动画。动画优先级隐式动画覆盖显式动画视图树中更靠后的胜出。Button(Tap){withAnimation(.linear){flag.toggle()}}.animation(.bouncy,value:flag)// .bouncy 胜出禁用动画// 阻止隐式动画覆盖.transaction{tint.disablesAnimationstrue}// 完全移除动画.transaction{$0.animationnil}自定义事务键iOS 17通过事务传递元数据。structChangeSourceKey:TransactionKey{staticletdefaultValue:Stringunknown}extensionTransaction{varchangeSource:String{get{self[ChangeSourceKey.self]}set{self[ChangeSourceKey.self]newValue}}}// 设置来源vartransactionTransaction(animation:.default)transaction.changeSourceserverwithTransaction(transaction){flag.toggle()}// 在视图树中读取.transaction{tinift.changeSourceserver{t.animation.smooth}else{t.animation.bouncy}}阶段动画iOS 17自动循环切换离散阶段。每个阶段变化都是一次独立的动画。基本用法// 好——触发的阶段动画Button(Shake){trigger1}.phaseAnimator([0.0,-10.0,10.0,-5.0,5.0,0.0],trigger:trigger){content,offsetincontent.offset(x:offset)}// 无限循环无触发Circle().phaseAnimator([1.0,1.2,1.0]){content,scaleincontent.scaleEffect(scale)}枚举阶段为清晰起见推荐// 好——枚举阶段自文档化enumBouncePhase:CaseIterable{caseinitial,up,down,settlevarscale:CGFloat{switchself{case.initial:1.0case.up:1.2case.down:0.9case.settle:1.0}}}Circle().phaseAnimator(BouncePhase.allCases,trigger:trigger){content,phaseincontent.scaleEffect(phase.scale)}每个阶段的自定义时序.phaseAnimator([0,-20,20],trigger:trigger){content,offsetincontent.offset(x:offset)}animation:{phaseinswitchphase{case-20:.bouncycase20:.lineardefault:.smooth}}好与坏// 好——多步序列使用 phaseAnimator.phaseAnimator([0,-10,10,0],trigger:trigger){content,offsetincontent.offset(x:offset)}// 坏——手动 DispatchQueue 排序Button(Animate){withAnimation(.easeOut(duration:0.1)){offset-10}DispatchQueue.main.asyncAfter(deadline:.now()0.1){withAnimation{offset10}}DispatchQueue.main.asyncAfter(deadline:.now()0.2){withAnimation{offset0}}}关键帧动画iOS 17在特定时间点提供精确值的精细时序控制。基本用法Button(Bounce){trigger1}.keyframeAnimator(initialValue:AnimationValues(),trigger:trigger){content,valueincontent.scaleEffect(value.scale).offset(y:value.verticalOffset)}keyframes:{_inKeyframeTrack(\.scale){SpringKeyframe(1.2,duration:0.15)SpringKeyframe(0.9,duration:0.1)SpringKeyframe(1.0,duration:0.15)}KeyframeTrack(\.verticalOffset){LinearKeyframe(-20,duration:0.15)LinearKeyframe(0,duration:0.25)}}structAnimationValues{varscale:CGFloat1.0varverticalOffset:CGFloat0}关键帧类型类型行为CubicKeyframe平滑插值LinearKeyframe直线插值SpringKeyframe弹簧物理MoveKeyframe瞬间跳跃无插值多个同步轨道轨道并行运行每个轨道动画化一个属性。// 好——带同步旋转和缩放的铃铛晃动structBellAnimation{varrotation:Double0varscale:CGFloat1.0}Image(systemName:bell.fill).keyframeAnimator(initialValue:BellAnimation(),trigger:trigger){content,valueincontent.rotationEffect(.degrees(value.rotation)).scaleEffect(value.scale)}keyframes:{_inKeyframeTrack(\.rotation){CubicKeyframe(15,duration:0.1)CubicKeyframe(-15,duration:0.1)CubicKeyframe(10,duration:0.1)CubicKeyframe(-10,duration:0.1)CubicKeyframe(0,duration:0.1)}KeyframeTrack(\.scale){CubicKeyframe(1.1,duration:0.25)CubicKeyframe(1.0,duration:0.25)}}// 坏——手动基于定时器的动画Image(systemName:bell.fill).onTapGesture{withAnimation(.easeOut(duration:0.1)){rotation15}DispatchQueue.main.asyncAfter(deadline:.now()0.1){withAnimation{rotation-15}}// ... 更多手动时序——容易出错}KeyframeTimelineiOS 17直接查询动画值用于测试或非 SwiftUI 场景。lettimelineKeyframeTimeline(initialValue:AnimationValues()){KeyframeTrack(\.scale){CubicKeyframe(1.2,duration:0.25)CubicKeyframe(1.0,duration:0.25)}}letmidpointtimeline.value(time:0.25)print(midpoint.scale)// 0.25 秒时的值动画完成处理程序iOS 17动画完成时执行代码。配合 withAnimation// 好——withAnimation 的完成处理Button(Animate){withAnimation(.spring){isExpanded.toggle()}completion:{showNextSteptrue}}配合事务用于重新执行// 好——每次触发变化时完成处理都会触发Circle().scaleEffect(bounceCount%20?1.0:1.2).transaction(value:bounceCount){transactionintransaction.animation.spring transaction.addAnimationCompletion{messageBounce\(bounceCount)complete}}// 坏——完成处理只触发一次没有 value 参数Circle().scaleEffect(bounceCount%20?1.0:1.2).animation(.spring,value:bounceCount).transaction{transactionin// 没有 valuetransaction.addAnimationCompletion{completionCount1// 永远只触发一次}}Animatable 宏iOS 26Animatable宏从所有可动画的存储属性自动合成animatableData消除了冗长的手动遵循代码。使用AnimatableIgnored排除不应动画化的属性。之前手动structWedge:Shape{varstartAngle:AnglevarendAngle:AnglevardrawClockwise:BoolvaranimatableData:AnimatablePairDouble,Double{get{AnimatablePair(startAngle.radians,endAngle.radians)}set{startAngle.radians(newValue.first)endAngle.radians(newValue.second)}}funcpath(inrect:CGRect)-Path{/* ... */}}之后AnimatableAnimatablestructWedge:Shape{varstartAngle:AnglevarendAngle:AngleAnimatableIgnoredvardrawClockwise:Boolfuncpath(inrect:CGRect)-Path{/* ... */}}何时使用优先使用Animatable适用于任何自定义Shape、AnimatableModifier或具有多个属性且遵循Animatable的类型使用AnimatableIgnored用于控制行为但不应插值的属性例如方向、标志、标识符宏适用于任何遵循Animatable的类型不只是Shape来源“What’s new in SwiftUI”WWDC25会话 256何时手动实现animatableData当插值值需要无法与存储属性一一对应的自定义逻辑时——归一化、钳制或驱动派生值——请使用显式的animatableData而不是宏。部署目标为 iOS 26 时使用AnimatableValues更早目标使用AnimatablePair。// iOS 26在插值期间将 phase 保持在 0..2π 并钳制 amplitudestructWaveShape:Shape{varamplitude:CGFloatvarphase:CGFloatvarmaxAmplitude:CGFloatvaranimatableData:AnimatableValuesCGFloat,CGFloat{get{AnimatableValues(amplitude,phase)}set{amplitudemin(max(newValue.value.0,0),maxAmplitude)phasenewValue.value.1.truncatingRemainder(dividingBy:2*.pi)}}funcpath(inrect:CGRect)-Path{/* ... */}}在更早的部署目标上同样的逻辑使用AnimatablePair配合newValue.first/newValue.second。快速参考事务所有 iOS 版本withTransaction是withAnimation的显式形式隐式动画覆盖显式动画视图树中更靠后的胜出使用disablesAnimations防止覆盖使用.transaction { $0.animation nil }移除动画自定义事务键iOS 17通过TransactionKey经动画系统传递元数据阶段动画iOS 17用于返回起点的多步序列为清晰起见优先使用枚举阶段每个阶段变化都是一次独立的动画一次性动画使用trigger参数关键帧动画iOS 17用于精确的时序控制轨道并行运行测试/高级用途使用KeyframeTimeline优先于手动 DispatchQueue 时序完成处理程序iOS 17一次性完成处理程序使用withAnimation(.animation) { } completion: { }需要在每次值变化时重新触发的处理程序使用.transaction(value:)没有value:参数时完成处理只触发一次Animatable 宏iOS 26使用Animatable从存储属性自动合成animatableData使用AnimatableIgnored排除不可动画的属性取代冗长的手动animatableDatagetter/setter
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑