iOS SwiftUI 动画开发指南 - 教程
<style>pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important; font-size: 14px !important; line-height: 1.6 !important; padding: 16px !important; margin: 16px 0 !important; background-color: rgba(248, 248, 248, 1) !important; border: 1px solid rgba(225, 228, 232, 1) !important; border-radius: 6px !important; tab-size: 4 !important; -moz-tab-size: 4 !important; max-width: 100% !important; box-sizing: border-box !important }code { font-family: "Consolas", "Monaco", "Courier New", monospace !important; font-size: 14px !important; white-space: pre !important; word-wrap: normal !important; word-break: normal !important; overflow-wrap: normal !important; display: inline !important; background: rgba(0, 0, 0, 0) !important; border: none !important; padding: 0 !important; margin: 0 !important; line-height: inherit !important }
pre code { background: rgba(0, 0, 0, 0) !important; border: 0 !important; border-radius: 0 !important; display: block !important; line-height: 1.6 !important; margin: 0 !important; max-width: none !important; overflow: visible !important; padding: 0 !important; white-space: pre !important; word-wrap: normal !important; word-break: normal !important; color: inherit !important }
.token.comment, .token.prolog, .token.doctype, .token.cdata { color: rgba(112, 128, 144, 1) !important; font-style: italic !important }
.token.punctuation { color: rgba(153, 153, 153, 1) !important }
.token.atrule, .token.attr-value, .token.keyword { color: rgba(0, 119, 170, 1) !important; font-weight: bold !important }
.token.function, .token.class-name { color: rgba(221, 74, 104, 1) !important; font-weight: bold !important }
.token.selector, .token.attr-name, .token.string, .token.char, .token.builtin, .token.inserted { color: rgba(102, 153, 0, 1) !important }
.token.property, .token.tag, .token.boolean, .token.number, .token.constant, .token.symbol, .token.deleted { color: rgba(153, 0, 85, 1) !important }
.cnblogs-markdown pre, .cnblogs-post-body pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; background-color: rgba(248, 248, 248, 1) !important; border: 1px solid rgba(225, 228, 232, 1) !important; border-radius: 6px !important; padding: 16px !important; margin: 16px 0 !important }
pre, pre, pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important }</style>
<div class="markdown_views prism-atom-one-dark" id="content_views"><svg style="display: none" xmlns="http://www.w3.org/2000/svg"><path d="M5,0 0,2.5 5,5z" id="raphael-marker-block" stroke-linecap="round" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0)"></path></svg><h2>iOS SwiftUI 动画开发指南</h2><p>为了把大家吸引过来,先上几张动画效果图:</p><p><strong>“引狼图”1</strong>:按钮点击 - 果冻动画效果<br><img src="https://i-blog.csdnimg.cn/direct/9aac92f4bb624d3083f8a78cc1240c12.gif"><br><strong>“引狼图”2</strong>:波纹动画效果<br><img src="https://i-blog.csdnimg.cn/direct/1b984a1e02224287b1b8e04fd75c9feb.gif"><br><strong>“引狼图”3</strong>:Lottie复杂动画效果<br><img src="https://i-blog.csdnimg.cn/direct/120aced94fe044cba700ced2e23a2eae.gif"></p><h3>1、动画概念</h3><p>SwiftUI 的动画是基于声明式的,当视图的状态发生变化时,系统会自动在旧状态和新状态之间插入中间帧,创建平滑的过渡效果。</p><blockquote><p><strong>工作原理:</strong><br>
状态驱动:动画由状态变化触发<br>
声明式语法:描述"做什么"而不是"怎么做"<br>
自动插值:系统自动计算中间值<br>
类型安全:编译时检查动画兼容性</p></blockquote><h5>1.1、隐式动画</h5><p>隐式动画通过 .animation() 修饰符自动应用于视图的所有可动画属性变化。当修饰符观察的值发生变化时,所有可动画的属性都会以指定的动画方式过渡。</p><p><strong>隐式动画特点:</strong></p><ul><li>使用 .animation() 修饰符</li><li>状态变化时自动动画</li><li>代码简洁,声明式</li></ul>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token keyword">import</span> <span class="token builtin">SwiftUI</span>
<span class="token keyword">struct</span> <span class="token builtin">SimpleImplicitAnimation</span><span class="token punctuation">:</span> <span class="token builtin">View</span> <span class="token punctuation">{</span>
@<span class="token builtin">State</span> <span class="token keyword">private</span> <span class="token keyword">var</span> isTapped <span class="token operator">=</span> <span class="token boolean">false</span>
<span class="token keyword">var</span> body<span class="token punctuation">:</span> some <span class="token builtin">View</span> <span class="token punctuation">{</span>
<span class="token function">VStack</span><span class="token punctuation">(</span>spacing<span class="token punctuation">:</span> <span class="token number">30</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// 圆形视图 - 点击后会放大并变色</span>
<span class="token function">Circle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">fill</span><span class="token punctuation">(</span>isTapped <span class="token operator">?</span> <span class="token punctuation">.</span>blue <span class="token punctuation">:</span> <span class="token punctuation">.</span>red<span class="token punctuation">)</span> <span class="token comment">// 颜色变化</span>
<span class="token punctuation">.</span><span class="token function">frame</span><span class="token punctuation">(</span>width<span class="token punctuation">:</span> isTapped <span class="token operator">?</span> <span class="token number">150</span> <span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">,</span> <span class="token comment">// 大小变化</span>
height<span class="token punctuation">:</span> isTapped <span class="token operator">?</span> <span class="token number">150</span> <span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">animation</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">easeInOut</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token number">0.5</span><span class="token punctuation">)</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> isTapped<span class="token punctuation">)</span> <span class="token comment">// 隐式动画</span>
<span class="token function">Button</span><span class="token punctuation">(</span><span class="token string">"点击动画"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
isTapped<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 状态变化自动触发动画</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h5>1.2、显式动画</h5><p>显式动画使用 withAnimation 闭包,只对闭包内的状态变化应用动画。可以精确控制哪些状态变化应该动画,以及使用什么样的动画参数。</p><p><strong>显式动画特点:</strong></p><ul><li>使用 withAnimation { } 闭包</li><li>精确控制哪些变化有动画</li><li>手动触发动画</li></ul>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token keyword">struct</span> <span class="token builtin">SimpleExplicitAnimation</span><span class="token punctuation">:</span> <span class="token builtin">View</span> <span class="token punctuation">{</span>
@<span class="token builtin">State</span> <span class="token keyword">private</span> <span class="token keyword">var</span> isTapped <span class="token operator">=</span> <span class="token boolean">false</span>
<span class="token keyword">var</span> body<span class="token punctuation">:</span> some <span class="token builtin">View</span> <span class="token punctuation">{</span>
<span class="token function">VStack</span><span class="token punctuation">(</span>spacing<span class="token punctuation">:</span> <span class="token number">30</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// 圆形视图</span>
<span class="token function">Circle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">fill</span><span class="token punctuation">(</span>isTapped <span class="token operator">?</span> <span class="token punctuation">.</span>green <span class="token punctuation">:</span> <span class="token punctuation">.</span>orange<span class="token punctuation">)</span> <span class="token comment">// 颜色变化</span>
<span class="token punctuation">.</span><span class="token function">frame</span><span class="token punctuation">(</span>width<span class="token punctuation">:</span> isTapped <span class="token operator">?</span> <span class="token number">150</span> <span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">,</span> <span class="token comment">// 大小变化</span>
height<span class="token punctuation">:</span> isTapped <span class="token operator">?</span> <span class="token number">150</span> <span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">)</span>
<span class="token comment">// 注意:这里没有 .animation() 修饰符</span>
<span class="token function">Button</span><span class="token punctuation">(</span><span class="token string">"点击动画"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// 使用 withAnimation 显式包装状态变化</span>
<span class="token function">withAnimation</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">easeInOut</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token number">0.5</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
isTapped<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 只有这个变化有动画</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h5>1.3、动画曲线</h5><p>在 SwiftUI 中,动画曲线(也称为时序曲线或缓动函数)定义了动画随时间变化的速度和节奏。它们让动画看起来更自然、更流畅。</p><p>1.3.1、线性动画 (Linear)</p><blockquote><p>匀速动画,速度保持不变。</p></blockquote>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token punctuation">.</span><span class="token function">animation</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">linear</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token number">1.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> value<span class="token punctuation">)</span></code></pre>
<p>1.3.2、缓动动画 (Ease)</p><blockquote><p>速度会变化,提供更自然的运动效果。<br>
包含:缓入(.easeIn)、缓出(.easeOut)、缓入缓出(.easeInOut,默认)</p></blockquote>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token punctuation">.</span><span class="token function">animation</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">easeInOut</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token number">1.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> value<span class="token punctuation">)</span>
<span class="token comment">// 开始慢 → 中间快 → 结束慢</span>
<span class="token comment">// 最自然的运动曲线</span></code></pre>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token punctuation">.</span><span class="token function">animation</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">easeIn</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token number">1.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> value<span class="token punctuation">)</span>
<span class="token comment">// 开始慢 → 逐渐加速到结束</span></code></pre>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token punctuation">.</span><span class="token function">animation</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">easeOut</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token number">1.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> value<span class="token punctuation">)</span>
<span class="token comment">// 开始快 → 逐渐减速到结束</span></code></pre>
<p>1.3.3、弹性动画 (Spring)</p><blockquote><p>模拟弹簧物理效果,带有回弹。</p></blockquote>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token punctuation">.</span><span class="token function">animation</span><span class="token punctuation">(</span>
<span class="token punctuation">.</span><span class="token function">spring</span><span class="token punctuation">(</span>
response<span class="token punctuation">:</span> <span class="token number">0.6</span><span class="token punctuation">,</span> <span class="token comment">// 持续时间</span>
dampingFraction<span class="token punctuation">:</span> <span class="token number">0.8</span><span class="token punctuation">,</span> <span class="token comment">// 阻尼(0-1,越小弹性越大)</span>
blendDuration<span class="token punctuation">:</span> <span class="token number">0.25</span> <span class="token comment">// 混合持续时间</span>
<span class="token punctuation">)</span><span class="token punctuation">,</span>
value<span class="token punctuation">:</span> value
<span class="token punctuation">)</span></code></pre>
<p>1.3.4、交互式弹簧 (Interpolating Spring)</p><blockquote><p>更精确控制弹簧物理参数的动画。</p></blockquote>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token punctuation">.</span><span class="token function">animation</span><span class="token punctuation">(</span>
<span class="token punctuation">.</span><span class="token function">interpolatingSpring</span><span class="token punctuation">(</span>
mass<span class="token punctuation">:</span> <span class="token number">1.0</span><span class="token punctuation">,</span> <span class="token comment">// 质量:影响惯性</span>
stiffness<span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">,</span> <span class="token comment">// 刚度:影响回弹力度</span>
damping<span class="token punctuation">:</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token comment">// 阻尼:影响阻力</span>
initialVelocity<span class="token punctuation">:</span> <span class="token number">0</span> <span class="token comment">// 初始速度</span>
<span class="token punctuation">)</span><span class="token punctuation">,</span>
value<span class="token punctuation">:</span> value
<span class="token punctuation">)</span></code></pre>
<p>1.3.5、自定义动画曲线<br>
timingCurve 是 SwiftUI 中用于创建自定义动画曲线的函数,它使用三次贝塞尔曲线来精确控制动画的加速和减速过程。</p>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token punctuation">.</span><span class="token function">animation</span><span class="token punctuation">(</span>
<span class="token punctuation">.</span><span class="token function">timingCurve</span><span class="token punctuation">(</span>
<span class="token number">0.25</span><span class="token punctuation">,</span> <span class="token comment">// 控制点1 X: 开始阶段的弯曲程度</span>
<span class="token number">0.1</span><span class="token punctuation">,</span> <span class="token comment">// 控制点1 Y: 开始阶段的速度</span>
<span class="token number">0.25</span><span class="token punctuation">,</span> <span class="token comment">// 控制点2 X: 结束阶段的弯曲程度</span>
<span class="token number">1.0</span><span class="token punctuation">,</span> <span class="token comment">// 控制点2 Y: 结束阶段的速度</span>
duration<span class="token punctuation">:</span> <span class="token number">1.0</span><span class="token comment">// 动画总时长</span>
<span class="token punctuation">)</span><span class="token punctuation">,</span>
value<span class="token punctuation">:</span> value
<span class="token punctuation">)</span></code></pre>
<h5>1.4、动画重复</h5><p>1.4.1、使用 repeatCount 指定重复次数</p>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token keyword">struct</span> <span class="token builtin">RepeatCountExample</span><span class="token punctuation">:</span> <span class="token builtin">View</span> <span class="token punctuation">{</span>
@<span class="token builtin">State</span> <span class="token keyword">private</span> <span class="token keyword">var</span> scale<span class="token punctuation">:</span> <span class="token builtin">CGFloat</span> <span class="token operator">=</span> <span class="token number">1.0</span>
<span class="token keyword">var</span> body<span class="token punctuation">:</span> some <span class="token builtin">View</span> <span class="token punctuation">{</span>
<span class="token function">Circle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">fill</span><span class="token punctuation">(</span><span class="token builtin">Color</span><span class="token punctuation">.</span>blue<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">frame</span><span class="token punctuation">(</span>width<span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">,</span> height<span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">scaleEffect</span><span class="token punctuation">(</span>scale<span class="token punctuation">)</span>
<span class="token punctuation">.</span>onAppear <span class="token punctuation">{</span>
<span class="token function">withAnimation</span><span class="token punctuation">(</span>
<span class="token punctuation">.</span><span class="token function">easeInOut</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token number">0.5</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">repeatCount</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span> <span class="token comment">// 重复3次</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
scale <span class="token operator">=</span> <span class="token number">1.5</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>1.4.2、使用 repeatForever 无限重复</p>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token keyword">struct</span> <span class="token builtin">RepeatForeverExample</span><span class="token punctuation">:</span> <span class="token builtin">View</span> <span class="token punctuation">{</span>
@<span class="token builtin">State</span> <span class="token keyword">private</span> <span class="token keyword">var</span> rotation<span class="token punctuation">:</span> <span class="token builtin">Double</span> <span class="token operator">=</span> <span class="token number">0</span>
<span class="token keyword">var</span> body<span class="token punctuation">:</span> some <span class="token builtin">View</span> <span class="token punctuation">{</span>
<span class="token function">Rectangle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">fill</span><span class="token punctuation">(</span><span class="token builtin">Color</span><span class="token punctuation">.</span>red<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">frame</span><span class="token punctuation">(</span>width<span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">,</span> height<span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">rotationEffect</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">degrees</span><span class="token punctuation">(</span>rotation<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span>onAppear <span class="token punctuation">{</span>
<span class="token function">withAnimation</span><span class="token punctuation">(</span>
<span class="token punctuation">.</span><span class="token function">linear</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token number">1.0</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">repeatForever</span><span class="token punctuation">(</span>autoreverses<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">)</span> <span class="token comment">// 无限重复,不自动反向</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
rotation <span class="token operator">=</span> <span class="token number">360</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>1.4.3、带自动反向的重复</p>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token keyword">struct</span> <span class="token builtin">AutoreverseExample</span><span class="token punctuation">:</span> <span class="token builtin">View</span> <span class="token punctuation">{</span>
@<span class="token builtin">State</span> <span class="token keyword">private</span> <span class="token keyword">var</span> offsetY<span class="token punctuation">:</span> <span class="token builtin">CGFloat</span> <span class="token operator">=</span> <span class="token number">0</span>
<span class="token keyword">var</span> body<span class="token punctuation">:</span> some <span class="token builtin">View</span> <span class="token punctuation">{</span>
<span class="token function">Circle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">fill</span><span class="token punctuation">(</span><span class="token builtin">Color</span><span class="token punctuation">.</span>green<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">frame</span><span class="token punctuation">(</span>width<span class="token punctuation">:</span> <span class="token number">80</span><span class="token punctuation">,</span> height<span class="token punctuation">:</span> <span class="token number">80</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">offset</span><span class="token punctuation">(</span>y<span class="token punctuation">:</span> offsetY<span class="token punctuation">)</span>
<span class="token punctuation">.</span>onAppear <span class="token punctuation">{</span>
<span class="token function">withAnimation</span><span class="token punctuation">(</span>
<span class="token punctuation">.</span><span class="token function">easeInOut</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token number">0.8</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">repeatForever</span><span class="token punctuation">(</span>autoreverses<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token comment">// 来回弹跳</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
offsetY <span class="token operator">=</span> <span class="token number">100</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h3>2、基础动画示例</h3><h5>2.1、位移动画 (Move)</h5>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token keyword">import</span> <span class="token builtin">SwiftUI</span>
<span class="token keyword">struct</span> <span class="token builtin">MoveAnimationSimple</span><span class="token punctuation">:</span> <span class="token builtin">View</span> <span class="token punctuation">{</span>
@<span class="token builtin">State</span> <span class="token keyword">private</span> <span class="token keyword">var</span> moveRight <span class="token operator">=</span> <span class="token boolean">false</span>
<span class="token keyword">var</span> body<span class="token punctuation">:</span> some <span class="token builtin">View</span> <span class="token punctuation">{</span>
<span class="token builtin">VStack</span> <span class="token punctuation">{</span>
<span class="token function">Circle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">fill</span><span class="token punctuation">(</span><span class="token punctuation">.</span>blue<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">frame</span><span class="token punctuation">(</span>width<span class="token punctuation">:</span> <span class="token number">80</span><span class="token punctuation">,</span> height<span class="token punctuation">:</span> <span class="token number">80</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">offset</span><span class="token punctuation">(</span>x<span class="token punctuation">:</span> moveRight <span class="token operator">?</span> <span class="token number">100</span> <span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token comment">// X轴位移</span>
<span class="token punctuation">.</span><span class="token function">animation</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">easeInOut</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token number">1.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> moveRight<span class="token punctuation">)</span>
<span class="token function">Button</span><span class="token punctuation">(</span><span class="token string">"移动"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
moveRight<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h5>2.2、旋转动画 (Rotation)</h5>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token keyword">struct</span> <span class="token builtin">RotationAnimationSimple</span><span class="token punctuation">:</span> <span class="token builtin">View</span> <span class="token punctuation">{</span>
@<span class="token builtin">State</span> <span class="token keyword">private</span> <span class="token keyword">var</span> rotate <span class="token operator">=</span> <span class="token boolean">false</span>
<span class="token keyword">var</span> body<span class="token punctuation">:</span> some <span class="token builtin">View</span> <span class="token punctuation">{</span>
<span class="token builtin">VStack</span> <span class="token punctuation">{</span>
<span class="token function">Rectangle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">fill</span><span class="token punctuation">(</span><span class="token punctuation">.</span>red<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">frame</span><span class="token punctuation">(</span>width<span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">,</span> height<span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">rotationEffect</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">degrees</span><span class="token punctuation">(</span>rotate <span class="token operator">?</span> <span class="token number">180</span> <span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 旋转角度</span>
<span class="token punctuation">.</span><span class="token function">animation</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">easeInOut</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token number">1.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> rotate<span class="token punctuation">)</span>
<span class="token function">Button</span><span class="token punctuation">(</span><span class="token string">"旋转"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
rotate<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h5>2.3、缩放动画 (Scale)</h5>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token keyword">struct</span> <span class="token builtin">ScaleAnimationSimple</span><span class="token punctuation">:</span> <span class="token builtin">View</span> <span class="token punctuation">{</span>
@<span class="token builtin">State</span> <span class="token keyword">private</span> <span class="token keyword">var</span> scaleUp <span class="token operator">=</span> <span class="token boolean">false</span>
<span class="token keyword">var</span> body<span class="token punctuation">:</span> some <span class="token builtin">View</span> <span class="token punctuation">{</span>
<span class="token builtin">VStack</span> <span class="token punctuation">{</span>
<span class="token function">Circle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">fill</span><span class="token punctuation">(</span><span class="token punctuation">.</span>green<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">frame</span><span class="token punctuation">(</span>width<span class="token punctuation">:</span> <span class="token number">80</span><span class="token punctuation">,</span> height<span class="token punctuation">:</span> <span class="token number">80</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">scaleEffect</span><span class="token punctuation">(</span>scaleUp <span class="token operator">?</span> <span class="token number">1.5</span> <span class="token punctuation">:</span> <span class="token number">1.0</span><span class="token punctuation">)</span> <span class="token comment">// 缩放比例</span>
<span class="token punctuation">.</span><span class="token function">animation</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">easeInOut</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token number">1.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> scaleUp<span class="token punctuation">)</span>
<span class="token function">Button</span><span class="token punctuation">(</span><span class="token string">"缩放"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
scaleUp<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h5>2.4、透明度动画 (Opacity)</h5>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token keyword">struct</span> <span class="token builtin">OpacityAnimationSimple</span><span class="token punctuation">:</span> <span class="token builtin">View</span> <span class="token punctuation">{</span>
@<span class="token builtin">State</span> <span class="token keyword">private</span> <span class="token keyword">var</span> show <span class="token operator">=</span> <span class="token boolean">false</span>
<span class="token keyword">var</span> body<span class="token punctuation">:</span> some <span class="token builtin">View</span> <span class="token punctuation">{</span>
<span class="token builtin">VStack</span> <span class="token punctuation">{</span>
<span class="token function">Circle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">fill</span><span class="token punctuation">(</span><span class="token punctuation">.</span>orange<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">frame</span><span class="token punctuation">(</span>width<span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">,</span> height<span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">opacity</span><span class="token punctuation">(</span>show <span class="token operator">?</span> <span class="token number">1.0</span> <span class="token punctuation">:</span> <span class="token number">0.3</span><span class="token punctuation">)</span> <span class="token comment">// 透明度</span>
<span class="token punctuation">.</span><span class="token function">animation</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">easeInOut</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token number">1.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> show<span class="token punctuation">)</span>
<span class="token function">Button</span><span class="token punctuation">(</span><span class="token string">"淡入/淡出"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
show<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h5>2.5、颜色动画 (Color)</h5>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token keyword">struct</span> <span class="token builtin">ColorAnimationSimple</span><span class="token punctuation">:</span> <span class="token builtin">View</span> <span class="token punctuation">{</span>
@<span class="token builtin">State</span> <span class="token keyword">private</span> <span class="token keyword">var</span> changeColor <span class="token operator">=</span> <span class="token boolean">false</span>
<span class="token keyword">var</span> body<span class="token punctuation">:</span> some <span class="token builtin">View</span> <span class="token punctuation">{</span>
<span class="token builtin">VStack</span> <span class="token punctuation">{</span>
<span class="token function">Rectangle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">fill</span><span class="token punctuation">(</span>changeColor <span class="token operator">?</span> <span class="token punctuation">.</span>purple <span class="token punctuation">:</span> <span class="token punctuation">.</span>blue<span class="token punctuation">)</span> <span class="token comment">// 颜色变化</span>
<span class="token punctuation">.</span><span class="token function">frame</span><span class="token punctuation">(</span>width<span class="token punctuation">:</span> <span class="token number">120</span><span class="token punctuation">,</span> height<span class="token punctuation">:</span> <span class="token number">120</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">animation</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">easeInOut</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token number">1.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> changeColor<span class="token punctuation">)</span>
<span class="token function">Button</span><span class="token punctuation">(</span><span class="token string">"变色"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
changeColor<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h5>2.6、圆角动画 (Corner Radius)</h5>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token keyword">struct</span> <span class="token builtin">CornerRadiusAnimationSimple</span><span class="token punctuation">:</span> <span class="token builtin">View</span> <span class="token punctuation">{</span>
@<span class="token builtin">State</span> <span class="token keyword">private</span> <span class="token keyword">var</span> roundCorners <span class="token operator">=</span> <span class="token boolean">false</span>
<span class="token keyword">var</span> body<span class="token punctuation">:</span> some <span class="token builtin">View</span> <span class="token punctuation">{</span>
<span class="token builtin">VStack</span> <span class="token punctuation">{</span>
<span class="token function">Rectangle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">fill</span><span class="token punctuation">(</span><span class="token punctuation">.</span>red<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">frame</span><span class="token punctuation">(</span>width<span class="token punctuation">:</span> <span class="token number">120</span><span class="token punctuation">,</span> height<span class="token punctuation">:</span> <span class="token number">120</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">cornerRadius</span><span class="token punctuation">(</span>roundCorners <span class="token operator">?</span> <span class="token number">60</span> <span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token comment">// 圆角变化</span>
<span class="token punctuation">.</span><span class="token function">animation</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">easeInOut</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token number">1.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> roundCorners<span class="token punctuation">)</span>
<span class="token function">Button</span><span class="token punctuation">(</span><span class="token string">"圆角"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
roundCorners<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h3>3、组合动画示例</h3><h5>3.1、多个属性同时动画</h5><p>同时为视图的多个属性应用动画</p>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token keyword">struct</span> <span class="token builtin">CombinedAnimationView</span><span class="token punctuation">:</span> <span class="token builtin">View</span> <span class="token punctuation">{</span>
@<span class="token builtin">State</span> <span class="token keyword">private</span> <span class="token keyword">var</span> isAnimating <span class="token operator">=</span> <span class="token boolean">false</span>
<span class="token keyword">var</span> body<span class="token punctuation">:</span> some <span class="token builtin">View</span> <span class="token punctuation">{</span>
<span class="token function">VStack</span><span class="token punctuation">(</span>spacing<span class="token punctuation">:</span> <span class="token number">30</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">"组合动画"</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">font</span><span class="token punctuation">(</span><span class="token punctuation">.</span>title<span class="token punctuation">)</span>
<span class="token function">RoundedRectangle</span><span class="token punctuation">(</span>cornerRadius<span class="token punctuation">:</span> isAnimating <span class="token operator">?</span> <span class="token number">50</span> <span class="token punctuation">:</span> <span class="token number">10</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">fill</span><span class="token punctuation">(</span>isAnimating <span class="token operator">?</span> <span class="token punctuation">.</span>blue <span class="token punctuation">:</span> <span class="token punctuation">.</span>red<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">frame</span><span class="token punctuation">(</span>
width<span class="token punctuation">:</span> isAnimating <span class="token operator">?</span> <span class="token number">200</span> <span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">,</span>
height<span class="token punctuation">:</span> isAnimating <span class="token operator">?</span> <span class="token number">200</span> <span class="token punctuation">:</span> <span class="token number">100</span>
<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">rotationEffect</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">degrees</span><span class="token punctuation">(</span>isAnimating <span class="token operator">?</span> <span class="token number">180</span> <span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">opacity</span><span class="token punctuation">(</span>isAnimating <span class="token operator">?</span> <span class="token number">0.8</span> <span class="token punctuation">:</span> <span class="token number">1.0</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">animation</span><span class="token punctuation">(</span>
<span class="token punctuation">.</span><span class="token function">easeInOut</span><span class="token punctuation">(</span>duration<span class="token punctuation">:</span> <span class="token number">1.5</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
value<span class="token punctuation">:</span> isAnimating
<span class="token punctuation">)</span>
<span class="token function">Button</span><span class="token punctuation">(</span><span class="token string">"触发组合动画"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
isAnimating<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">.</span><span class="token function">padding</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h5>3.2、序列动画</h5><p>使用多个 withAnimation 创建顺序执行的动画</p><p>此处模拟实现按钮点击 - 果冻动画效果:<br><img src="https://i-blog.csdnimg.cn/direct/40f9e669f27a439da2f967d18a0bc1ad.gif"></p>
<pre style="white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important"><code class="prism language-swift"><span class="token keyword">struct</span> <span class="token builtin">Anim1</span><span class="token punctuation">:</span> <span class="token builtin">View</span> <span class="token punctuation">{</span>
@<span class="token builtin">State</span> <span class="token keyword">private</span> <span class="token keyword">var</span> scaleX <span class="token operator">=</span> <span class="token number">1.0</span>
@<span class="token builtin">State</span> <span class="token keyword">private</span> <span class="token keyword">var</span> scaleY <span class="token operator">=</span> <span class="token number">1.0</span>
<span class="token keyword">var</span> body<span class="token punctuation">:</span> some <span class="token builtin">View</span> <span class="token punctuation">{</span>
<span class="token function">Button</span><span class="token punctuation">(</span><span class="token string">"button"</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token function">doAnim</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">.</span><span class="token function">foregroundColor</span><span class="token punctuation">(</span><span class="token punctuation">.</span>white<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">padding</span><span class="token punctuation">(</span><span class="token punctuation">.</span>horizontal<span class="token punctuation">,</span> <span class="token number">50</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">padding</span><span class="token punctuation">(</span><span class="token punctuation">.</span>vertical<span class="token punctuation">,</span> <span class="token number">40</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">background</span><span class="token punctuation">(</span><span class="token builtin">Color</span><span class="token punctuation">.</span>blue<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">cornerRadius</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">scaleEffect</span><span class="token punctuation">(</span>x<span class="token punctuation">:</span>scaleX<span class="token punctuation">,</span> y<span class="token punctuation">:</span> scaleY<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">func</span> <span class="token function">doAnim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token builtin">Task</span><span class="token punctuation">{</span>@<span class="token builtin">MainActor</span> <span class="token keyword">in</span>
<span class="token function">withAnimation</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">spring</span><span class="token punctuation">(</span>response<span class="token punctuation">:</span><span class="token number">0.1</span><span class="token punctuation">,</span> dampingFraction<span class="token punctuation">:</span> <span class="token number">0.8</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
scaleX <span class="token operator">=</span> <span class="token number">0.8</span>
scaleY <span class="token operator">=</span> <span class="token number">1.2</span>
<span class="token punctuation">}</span>
await <span class="token keyword">try</span> <span class="token builtin">Task</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token keyword">for</span><span class="token punctuation">:</span> <span class="token punctuation">.</span><span class="token function">seconds</span><span class="token punctuation">(</span><span class="token number">0.1</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token function">withAnimation</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">interpolatingSpring</span><span class="token punctuation">(</span>mass<span class="token punctuation">:</span> <span class="token number">1.0</span><span class="token punctuation">,</span> stiffness<span class="token punctuation">:</span> <span class="token number">200</span><span class="token punctuation">,</span> damping<span class="token punctuation">:</span> <span class="token number">8</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
scaleX <span class="token operator">=</span> <span class="token number">1.0</span>
scaleY <span class="token operator">=</span> <span class="token number">1.0</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h3>4、Lottie动画</h3><p>使用SwiftUI动画可实现基础的动画效果,如果动画包含复杂逻辑,建议使用Lottie实现(线上有很多的lottie动画文件(.json)可直接使用)<br>
Lottie 是一个由 Airbnb 开发的开源库,用于在 iOS、Android 和 Web 上展示高质量的动画。使用 Lottie 动画能够为你的 SwiftUI 应用增添更丰富的交互和视觉效果,同时保持良好的性能和用户体验。</p><blockquote><p>SwiftUI 中使用 Lottie 动画的一般步骤:<br>
1、安装 Lottie 库:可以使用Swift Package Manager 或者 CocoaPods<br>
2、SwiftUI 视图导入 Lottie 模块:import Lottie<br>
3、Lottie 动画 JSON 文件添加到你的项目中<br>
4、SwiftUI 视图中使用 LottieView加载动画文件</p></blockquote><p><img src="https://i-blog.csdnimg.cn/direct/467d8de1b8684dab99ce129f19ec895a.png"><br>
json文件管理、动画加载参考:<br><img src="https://i-blog.csdnimg.cn/direct/1e26ee51daa84d29b96584783a7c49f9.png"><br><img src="https://i-blog.csdnimg.cn/direct/fb69c4e173bd427aba14f23baf42c820.gif"></p></div><br><br>
来源:https://www.cnblogs.com/gccbuaa/p/19332720
頁:
[1]