如何解决如何在 .animation() 期间访问 @Binding 的当前值
如何访问此动画属性 scaleAmount
的当前值?
struct ContentView: View {
@State private var scaleAmount: CGFloat = 1
var body: some View {
vstack {
Stepper("Scale amount",value: $scaleAmount.animation(),in: 1...5)
Circle()
.fill(Color.red)
.frame(width: 50,height: 50)
.scaleEffect(scaleAmount)
.onChange(of: scaleAmount) { value in
print(value)
}
}
}
}
输出:
2.0
3.0
4.0
5.0
这些是可动画属性的最终值 - 而不是用于动画的内插值。这些就是我需要的。
可以这样做吗?我还尝试将 .onChange
块替换为
.onChange(of: $scaleAmount.animation()) { value in
print(value)
}
但是编译器说 .animation()
调用返回的 Binding 必须符合 Equatable。
我还尝试将 Circle 视图分解为自定义视图,并只为其提供一个 CGFloat 来使用,然后动画就会发生。但是绘制圆圈的视图中没有绑定。这给出了与第一个代码片段相同的输出。
struct ContentView: View {
@State private var scaleAmount: CGFloat = 1
var body: some View {
vstack {
Stepper("Scale amount",in: 1...5)
MyCircle(scaleAmount: scaleAmount)
}
}
}
struct MyCircle: View {
var scaleAmount: CGFloat
var body: some View {
print(scaleAmount)
return Circle()
.fill(Color.red)
.frame(width: 50,height: 50)
.scaleEffect(scaleAmount)
}
}
那么,问题又来了:是否可以在动画中获取 scaleAmount
属性的当前值?
解决方法
//
// Created by Eric Lightfoot on 2021-06-24.
//
import SwiftUI
/// Use .onBody(observedValue:onBody:) by passing an
/// animatable property along with a closure to execute
/// whenever that property is changed throughout the
/// animation
/// ---
// struct ContentView: View {
// @State private var scaleAmount: CGFloat = 1
// @State var scaleText: String = "1.000"
//
// var body: some View {
// VStack {
// Stepper("Scale amount",value: $scaleAmount.animation(),in: 1...5)
// ZStack {
// Circle()
// .fill(Color.red)
// .frame(width: 50,height: 50)
// Text(scaleText)
// .onBody(for: scaleAmount,onBody: { scaleText = "\($0)" })
// }
// .scaleEffect(scaleAmount)
// }
// }
// }
/// ---
struct AnimationProgressObserverModifier<Value>: AnimatableModifier where Value: VectorArithmetic {
/// Some experimenting led to observing the behaviour
/// that wrapping the observedValue with animatableData
/// (to conform to the protocol) caused this property
/// to be set continuously during the animation,instead
/// of only at the end
var animatableData: Value {
get {
observedValue
}
set {
observedValue = newValue
runCallbacks()
}
}
/// The animatable property to be observed
/// passed in at init
private var observedValue: Value
/// The block to execute on each animation progress
/// update
private var onBody: (Value) -> Void
init (observedValue: Value,onBody: @escaping (Value) -> Void) {
self.onBody = onBody
self.observedValue = observedValue
}
/// Run the onBody block provided at init
/// specifically passing animatable data,/// NOT observedValue
private func runCallbacks () {
DispatchQueue.main.async {
onBody(animatableData)
}
}
func body (content: Content) -> some View {
/// No modification to view content
content
}
}
extension View {
/// Convenience method on View
func onBody<Value: VectorArithmetic> (for value: Value,onBody: @escaping (Value) -> Void)
-> ModifiedContent<Self,AnimationProgressObserverModifier<Value>> {
modifier(AnimationProgressObserverModifier(observedValue: value,onBody: onBody))
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。