如何利用rect值实现SwiftUI路径动画?遇不可变属性错误求助
SwiftUI路径动画实现问题
我想用代码实现路径动画,当前ContentView代码如下:
struct ContentView: View { @State private var update: Bool = false var body: some View { VStack(spacing: 10.0) { VStack { MyShape(value: update) .stroke(style: StrokeStyle(lineWidth: 5.0, lineCap: .round, lineJoin: .round)) .padding() .animation(.default, value: update) } .frame(width: 300.0, height: 300.0) .background(Color.gray) .cornerRadius(10.0) Button("Update") { update.toggle() } } .padding(10.0) } }
尝试用animatableData实现动画但未生效,想把Bool值转换为CGFloat或Double时出现如下错误:
Cannot assign to property: 'self' is immutable
对应的MyShape代码如下:
struct MyShape: Shape { var value: Bool private var dynamicValue: CGFloat = .zero var animatableData: CGFloat { get { return dynamicValue } set(newValue) { dynamicValue = newValue } } func path(in rect: CGRect) -> Path { if value { dynamicValue = rect.minY } else { dynamicValue = rect.maxY } return Path { path in path.move(to: CGPoint(x: rect.minX, y: rect.midY)) path.addLine(to: CGPoint(x: rect.midX, y: dynamicValue)) path.addLine(to: CGPoint(x: rect.maxX , y: rect.midY)) } } }
当前效果:
问题解决
错误原因
在Shape的path(in:)方法中修改dynamicValue属性会报错,因为struct是值类型,path是实例方法,方法内的self是不可变的,无法修改结构体属性。
正确实现
我们需要将Bool值映射为0-1的CGFloat作为animatableData,动画过程中系统会自动插值这个值,再根据插值计算路径的坐标,实现平滑动画。修改后的MyShape代码如下:
struct MyShape: Shape { var value: Bool // 将Bool映射为0/1的CGFloat,作为动画插值的载体 var animatableData: CGFloat { get { value ? 1 : 0 } set { value = newValue > 0.5 } } func path(in rect: CGRect) -> Path { // 根据animatableData的插值,计算当前的Y坐标 let progress = animatableData // 从maxY向minY过渡:progress为0时取maxY,为1时取minY let currentY = rect.maxY - progress * (rect.maxY - rect.minY) return Path { path in path.move(to: CGPoint(x: rect.minX, y: rect.midY)) path.addLine(to: CGPoint(x: rect.midX, y: currentY)) path.addLine(to: CGPoint(x: rect.maxX, y: rect.midY)) } } }
说明
animatableData负责在动画过程中传递0到1的插值,系统会自动处理动画的中间帧- 在
path方法中,根据插值计算currentY,实现中间点在rect.minY和rect.maxY之间平滑移动 - 无需额外修改
ContentView,点击按钮后即可看到平滑的路径动画效果
内容的提问来源于stack exchange,提问作者swiftPunk
相关产品推荐
相关产品推荐

