SwiftUI 奇怪的动画行为与 systemImage
Posted
技术标签:
【中文标题】SwiftUI 奇怪的动画行为与 systemImage【英文标题】:SwiftUI odd animation behavior with systemImage 【发布时间】:2022-01-06 16:15:58 【问题描述】:我在 SwiftUI 中处理有趣的动画时遇到了一个奇怪的问题,涉及对 SwiftUI 的 SF 符号进行动画更改。基本上,我想为一组扩大的圆圈制作动画,这些圆圈随着它们越远而失去不透明度。当我使用Circle()
形状为圆圈设置动画时,这可以正常工作,但是当我使用Image(systemName: "circle")
时会抛出一个奇怪的错误。即,它抛出No symbol named 'circle' found in system symbol set
,我在Xcode 中得到了可怕的“紫色”错误。为什么我的动画适用于形状而不适用于 SF 符号?
带有形状的动画代码:
struct ContentView: View
let timer = Timer.publish(every: 0.25, on: .main, in: .common).autoconnect()
@State var firstIndex: Int = 0
@State var secondIndex: Int = 10
@State var thirdIndex: Int = 20
@State var fourthIndex: Int = 30
private func changeIndex(index: Int) -> Int
if index == 40
return 0
else
return index + 1
var body: some View
ZStack
Circle()
.foregroundColor(.black)
.frame(width: 10, height: 10)
ExpandingCircle(index: firstIndex)
ExpandingCircle(index: secondIndex)
ExpandingCircle(index: thirdIndex)
ExpandingCircle(index: fourthIndex)
.onReceive(timer) time in
withAnimation(.linear(duration: 0.25))
self.firstIndex = changeIndex(index: firstIndex)
self.secondIndex = changeIndex(index: secondIndex)
self.thirdIndex = changeIndex(index: thirdIndex)
self.fourthIndex = changeIndex(index: fourthIndex)
其中ExpandingCircle
定义为:
struct ExpandingCircle: View
let index: Int
private func getSize() -> CGFloat
return CGFloat(index * 2)
private func getOpacity() -> CGFloat
if index == 0 || index == 40
return 0
else
return CGFloat(1 - (Double(index) * 0.025))
var body: some View
Circle()
.strokeBorder(Color.red, lineWidth: 4)
.frame(width: getSize(), height: getSize())
.opacity(getOpacity())
要复制错误,请将ExpandingCircle
中的ContentView
替换为ExpandingCircleImage
:
struct ExpandingCircleImage: View
let index: Int
private func getSize() -> CGFloat
return CGFloat(index * 2)
private func getOpacity() -> CGFloat
if index == 0 || index == 40
return 0
else
return CGFloat(1 - (Double(index) * 0.025))
var body: some View
Image(systemName: "circle")
.foregroundColor(.red)
.font(.system(size: getSize()))
.opacity(getOpacity())
【问题讨论】:
【参考方案1】:您的ExpandingCircleImage
令人窒息,因为您不能拥有大小为 0 的系统字体,并且您一直试图将 0 输入到您的 ExpandingCircleImage
视图中。但是,除此之外,您不需要使用计时器来驱动动画。事实上,它使动画看起来很奇怪,因为计时器不准确。接下来,您的ExpandingCircle
或ExpandingCircleImage
应该为自己设置动画并成为完整的效果。
修复 font size = 0 问题时您将遇到的下一个问题是 .font(.system(size:))
不能按原样设置动画。你需要为它写一个AnimatableModifier
。看起来像这样:
struct AnimatableSfSymbolFontModifier: AnimatableModifier
var size: CGFloat
var animatableData: CGFloat
get size
set size = newValue
func body(content: Content) -> some View
content
.font(.system(size: size))
extension View
func animateSfSymbol(size: CGFloat) -> some View
self.modifier(AnimatableSfSymbolFontModifier(size: size))
animatableData
变量是关键。它教 SwiftUI 改变什么来渲染动画。在这种情况下,我们正在为字体大小设置动画。视图扩展只是为了方便,所以我们可以使用.
表示法。
为像这样的视图设置动画的另一个技巧是拥有多个动画,这些动画只是整体的一部分。换句话说,如果你使用四个圆圈,第一个到 25%,第二个从 25% 到 50%,然后是 50% 到 75%,最后是 75% 到 100%。你似乎也希望戒指随着膨胀而褪色,所以我也把它写进去了。下面的代码将有两个动画视图,一个是用形状制作的,一个是用 SF Symbol 制作的。
struct ContentView: View
var body: some View
VStack
Spacer()
ZStack
Circle()
.foregroundColor(.black)
.frame(width: 10, height: 10)
ExpandingCircle(maxSize: 100)
.frame(height: 100)
Spacer()
ZStack
Circle()
.foregroundColor(.black)
.frame(width: 10, height: 10)
ExpandingCircleImage(maxSize: 100)
.frame(height: 100)
Spacer()
struct ExpandingCircle: View
let maxSize: CGFloat
@State private var animate = false
var body: some View
ZStack
Circle()
.strokeBorder(Color.red, lineWidth: 8)
.opacity(animate ? 0.75 : 1)
.scaleEffect(animate ? 0.25 : 0)
Circle()
.strokeBorder(Color.red, lineWidth: 8)
.opacity(animate ? 0.5 : 0.75)
.scaleEffect(animate ? 0.5 : 0.25)
Circle()
.strokeBorder(Color.red, lineWidth: 8)
.opacity(animate ? 0.25 : 0.5)
.scaleEffect(animate ? 0.75 : 0.5)
Circle()
.strokeBorder(Color.red, lineWidth: 8)
.opacity(animate ? 0 : 0.25)
.scaleEffect(animate ? 1 : 0.75)
.frame(width: maxSize, height: maxSize)
.onAppear
withAnimation(.linear(duration: 4).repeatForever(autoreverses: false))
animate = true
struct ExpandingCircleImage: View
let maxSize: CGFloat
@State private var animate = false
var body: some View
ZStack
Image(systemName: "circle")
.animateSfSymbol(size: animate ? (maxSize * 0.25) : 1)
.opacity(animate ? 0.75 : 1)
Image(systemName: "circle")
.animateSfSymbol(size: animate ? (maxSize * 0.5) : (maxSize * 0.25))
.opacity(animate ? 0.5 : 0.75)
Image(systemName: "circle")
.animateSfSymbol(size: animate ? (maxSize * 0.75) : (maxSize * 0.5))
.opacity(animate ? 0.25 : 0.5)
Image(systemName: "circle")
.animateSfSymbol(size: animate ? (maxSize) : (maxSize * 0.75))
.opacity(animate ? 0 : 0.25)
.foregroundColor(.red)
.onAppear
withAnimation(.linear(duration: 4).repeatForever(autoreverses: false))
animate = true
记得在你的代码中包含AnimatableModifier
。
【讨论】:
这很棒而且非常彻底!谢谢以上是关于SwiftUI 奇怪的动画行为与 systemImage的主要内容,如果未能解决你的问题,请参考以下文章