我正在使用以下代码来显示带有圆圈和复选标记的成功
struct CheckMarkView: View {
@State var borderInit: Bool = false
@State var spinArrow: Bool = false
@State var dismissArrow: Bool = false
@State var displayCheckmark: Bool = false
var body: some View {
ZStack {
Circle()
.strokeBorder(style: StrokeStyle(lineWidth: borderInit ? 10 : 64))
.frame(width: 128, height: 128)
.foregroundColor(borderInit ? .white : .black)
.animation(.easeOut(duration: 3).speed(1.5))
.onAppear() {
borderInit.toggle()
}
// Arrow Icon
Image(systemName: "arrow.2.circlepath")
.frame(width: 80, height: 80)
.font(.largeTitle)
.foregroundColor(.white)
.rotationEffect(.degrees(spinArrow ? 360 : -360))
.opacity(dismissArrow ? 0 : 1)
.animation(.easeOut(duration: 2))
.onAppear() {
spinArrow.toggle()
withAnimation(Animation.easeInOut(duration: 1).delay(1)) {
self.dismissArrow.toggle()
}
}
// Checkmark
Path { path in
path.move(to: CGPoint(x: 20, y: -40))
path.addLine(to: CGPoint(x: 40, y: -20))
path.addLine(to: CGPoint(x: 80, y: -60))
}
.trim(from: 0, to: displayCheckmark ? 1 : 0)
.stroke(style: StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
.foregroundColor(displayCheckmark ? .white : .black)
.offset(x: 150, y: 420)
// .animation(.spring(.bouncy, blendDuration: 2).delay(2))
.animation(Animation.interpolatingSpring(stiffness: 150, damping: 10, initialVelocity: 0).delay(2))
// .animation(Animation.interpolatingSpring(mass: 1, stiffness: 100, damping: 10, initialVelocity: 0).delay(2))
.onAppear() {
displayCheckmark.toggle()
}
}
.background(.black)
}
}
#Preview {
CheckMarkView()
.frame(width: 128, height: 128)
}
当我尝试向 CheckMarkView 添加框架时,图像和圆圈显示在不同的位置。复选标记应显示在圆圈内。另外复选标记动画最后也不平滑。如何解决这个问题?任何帮助将不胜感激。
您应该将复选标记打入
Shape
。这样,SwiftUI 就会为您处理布局,您无需使用 offset
将其放置在正确的位置。
这是一个
Shape
实施示例,基于您的 Path
中的要点
struct CheckmarkShape: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
let fifthWidth = rect.width / 5
let quarterHeight = rect.height / 4
path.move(to: .init(x: rect.minX + fifthWidth, y: rect.minY + 2 * quarterHeight))
path.addLine(to: .init(x: rect.minX + 2 * fifthWidth, y: rect.minY + 3 * quarterHeight))
path.addLine(to: .init(x: rect.minX + 4 * fifthWidth, y: rect.minY + quarterHeight))
return path
}
}
CheckmarkView
看起来像这样:
@State var borderInit: Bool = false
@State var spinArrow: Bool = false
@State var dismissArrow: Bool = false
@State var displayCheckmark: Bool = false
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
Circle()
.strokeBorder(style: StrokeStyle(lineWidth: borderInit ? 10 : 64))
.frame(width: 128, height: 128)
.foregroundColor(borderInit ? .white : .black)
.animation(.easeOut(duration: 3).speed(1.5), value: borderInit)
.onAppear() {
borderInit.toggle()
}
// Arrow Icon
Image(systemName: "arrow.2.circlepath")
.frame(width: 80, height: 80)
.font(.largeTitle)
.foregroundColor(.white)
.rotationEffect(.degrees(spinArrow ? 360 : -360))
.opacity(dismissArrow ? 0 : 1)
.animation(.easeOut(duration: 2), value: dismissArrow)
.animation(.easeOut(duration: 2), value: spinArrow)
.onAppear() {
spinArrow.toggle()
withAnimation(Animation.easeInOut(duration: 1).delay(1)) {
self.dismissArrow.toggle()
}
}
CheckmarkShape()
.trim(from: 0, to: displayCheckmark ? 1 : 0)
.stroke(style: StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
.frame(width: 100, height: 100)
.foregroundColor(displayCheckmark ? .white : .black)
.animation(.bouncy.delay(2), value: displayCheckmark)
.onAppear() {
displayCheckmark.toggle()
}
}
}
您应该将
value
参数传递给所有 animation
修饰符。不推荐使用没有 value
的版本。
由于您使用了弹簧动画,我假设您希望复选标记的笔划稍微超出它应该结束的位置,然后弹回来。您可以通过在
Shape
实现中扩展箭头的路径并将 trim
设置为小于 1 的动画来实现此目的。
struct CheckmarkShape: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
let fifthWidth = rect.width / 5
let quarterHeight = rect.height / 4
path.move(to: .init(x: rect.minX + fifthWidth, y: rect.minY + 2 * quarterHeight))
path.addLine(to: .init(x: rect.minX + 2 * fifthWidth, y: rect.minY + 3 * quarterHeight))
path.addLine(to: .init(x: rect.minX + 5 * fifthWidth, y: rect.minY))
return path
}
}
.trim(from: 0, to: displayCheckmark ? 0.75 : 0)