SwiftUI中事件触发的动画

时间:2019-07-29 11:17:18

标签: animation swiftui

SwiftUI动画通常由状态驱动,这很棒,但是有时候您真的想触发一个临时(通常是可逆的)动画来响应某些事件。例如,我想在点击按钮时临时增加按钮的大小(释放按钮时,大小的增加和减少都应作为单个动画发生),但是我无法弄清楚

我认为它可以与过渡一起被黑客入侵,但不是很好。另外,如果我制作使用自动反转的动画,它将增大大小,减小其大小,然后跳回增大状态。

4 个答案:

答案 0 :(得分:1)

您可以使用与longPressAction()关联的@State变量:

enter image description here

针对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时无效

它的效果很好,它不仅适用于所有视图,而且还适用于所有视图,无需依赖触摸事件或按钮样式,但在我看来仍然有点像黑客。

具有随机旋转和缩放效果的示例:

enter image description here

代码示例:

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或其他复杂的解决方法。

enter image description here

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中的错误。