SwiftUI动画通常由状态驱动,这很棒,但是有时候您真的想触发一个临时(通常是可逆的)动画来响应某些事件。例如,我想在点击按钮时临时增加按钮的大小(释放按钮时,大小的增加和减少都应作为单个动画发生),但是我无法弄清楚
我认为它可以与过渡一起被黑客入侵,但不是很好。另外,如果我制作使用自动反转的动画,它将增大大小,减小其大小,然后跳回增大状态。
答案 0 :(得分:1)
您可以使用与longPressAction()关联的@State
变量:
针对Beta 5的代码已更新:
struct ContentView: View {
var body: some View {
HStack {
Spacer()
MyButton(label: "Button 1")
Spacer()
MyButton(label: "Button 2")
Spacer()
MyButton(label: "Button 3")
Spacer()
}
}
}
struct MyButton: View {
let label: String
@State private var pressed = false
var body: some View {
return Text(label)
.font(.title)
.foregroundColor(.white)
.padding(10)
.background(RoundedRectangle(cornerRadius: 10).foregroundColor(.green))
.scaleEffect(self.pressed ? 1.2 : 1.0)
.onLongPressGesture(minimumDuration: .infinity, maximumDistance: .infinity, pressing: { pressing in
withAnimation(.easeInOut(duration: 0.2)) {
self.pressed = pressing
}
}, perform: { })
}
}
答案 1 :(得分:1)
这也是我一直在研究的东西。
到目前为止,我的解决方案依赖于应用 GeometryEffect 修改器,并滥用在某些动画过程中连续调用其方法 effectValue 的事实。因此,所需的效果实际上是从0..1插值的变换,其主要效果为0.5,而在0或1时无效
它的效果很好,它不仅适用于所有视图,而且还适用于所有视图,无需依赖触摸事件或按钮样式,但在我看来仍然有点像黑客。
具有随机旋转和缩放效果的示例:
代码示例:
struct ButtonEffect: GeometryEffect {
var offset: Double // 0...1
var animatableData: Double {
get { offset }
set { offset = newValue }
}
func effectValue(size: CGSize) -> ProjectionTransform {
let effectValue = abs(sin(offset*Double.pi))
let scaleFactor = 1+0.2*effectValue
let affineTransform = CGAffineTransform(rotationAngle: CGFloat(effectValue)).translatedBy(x: -size.width/2, y: -size.height/2).scaledBy(x: CGFloat(scaleFactor), y: CGFloat(scaleFactor))
return ProjectionTransform(affineTransform)
}
}
struct ButtonActionView: View {
@State var animOffset: Double = 0
var body: some View {
Button(action:{
withAnimation(.spring()) {
self.animOffset += 1
}
})
{
Text("Press ME")
.padding()
}
.background(Color.yellow)
.modifier(ButtonEffect(offset: animOffset))
}
}
答案 2 :(得分:1)
我相信这就是你所追求的。 (这就是我解决此问题的方式)
基于dfd中的链接,我提出了这个,它不依赖于任何@State变量。您只需实现自己的按钮样式即可。 无需使用Timers,@ Binding,@ State或其他复杂的解决方法。
import SwiftUI
struct MyCustomPressButton: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.padding(10)
.cornerRadius(10)
.scaleEffect(configuration.isPressed ? 0.8 : 1.0)
}
}
struct Play: View {
var body: some View {
Button("Tap") {
}.buttonStyle(MyCustomPressButton())
.animation(.easeIn(duration: 0.2))
}
}
struct Play_Previews: PreviewProvider {
static var previews: some View {
Play()
}
}
答案 3 :(得分:0)
在SwiftUI中不需要通过状态更新。您需要具有一些仅在短时间内为真的属性,然后再切换回去。
以下动画从小到大,然后再动画。
struct ViewPlayground: View {
@State var enlargeIt = false
var body: some View {
Button("Event!") {
withAnimation {
self.enlargeIt = true
}
}
.background(Momentary(doIt: self.$enlargeIt))
.scaleEffect(self.enlargeIt ? 2.0 : 1.0)
}
}
struct Momentary: View {
@Binding var doIt: Bool
var delay: TimeInterval = 0.35
var body: some View {
Group {
if self.doIt {
ZStack { Spacer() }
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + self.delay) {
withAnimation {
self.doIt = false
}
}
}
}
}
}
}
很遗憾,delay
对于设置self.enlargeIt = true
时出现动画是必需的。没有它,它只会使动画回退。不知道这是否是Beta 4中的错误。