首页 > 解决方案 > 如何修复自定义按钮在 SwiftUI(IOS) 中的交互?

问题描述

我像这样在 SwiftUi(ios) 中创建了一个自定义按钮,它有自己的交互,但是当我将它与其他两个动画一起使用时,按钮的交互被新的交互所取代

这是自定义按钮代码


import SwiftUI

public struct fillCircular: ButtonStyle {

    var width: CGFloat
    var height: CGFloat = 50

    public func makeBody(configuration: Self.Configuration) -> some View {

        configuration.label
            .padding(.horizontal)
            .frame(width: width, height: height)
            .background(Capsule(style: .circular).fill(Color.primary))
            .font(.titleChanga3)
            .foregroundColor(Color.white)
            .shadow(color: Color.primary
                .opacity(configuration.isPressed ? 0.55 : 0.34),
                    radius: configuration.isPressed ? 1 : 4,
                    x: 0, y: configuration.isPressed ? 1 : 3)
            .scaleEffect(CGSize(width: configuration.isPressed ? 0.99 : 1,
                                height: configuration.isPressed ? 0.99 : 1))
            .animation(.spring())
    }
}

这就是问题所在

struct Welcome: View {

    @State var start: Bool = false

    var body: some View {
           GeometryReader { g in 
               Button(action: {}) {
                   Text("New Account")
               }
               .buttonStyle(fillCircular(width: g.size.width - (24 * 2)))
               .offset(y: self.start ? 0 : 20)
               .opacity(self.start ? 1 : 0)
               .animation(Animation.easeOut(duration: 0.5).delay(0.4))

          }

    }

添加此方法后,自定义按钮(fillCircular)中的动画会发生变化

.animation(Animation.easeOut(duration: 0.5).delay(0.4))

如何进入Animation同时保持自定义按钮的交互

标签: iosbuttonswiftuiios13

解决方案


更新的答案

基本上,您的第二个.animation()电话会覆盖第一个电话。不要应用第二个动画,而是将你start = truewithAnimation()调用包装起来,如下所示:

struct WelcomeView: View {

    @State var start: Bool = false

    var body: some View {
        VStack {
            // This is just here to toggle start variable
            Button("Toggle", action: {
                withAnimation(Animation.easeOut(duration: 0.5).delay(0.4)) {
                    self.start.toggle()
                }
            })

            GeometryReader { g in
                Button(action: {}) {
                    Text("New Account")
                }
                .buttonStyle(fillCircular(width: g.size.width - (24 * 2)))
                .offset(y: self.start ? 0 : 20)
                .opacity(self.start ? 1 : 0)
            }
        }
    }
}

然后easeOut动画将仅在调用按钮的动作闭包时应用。

这个答案更简洁,但原版也适用于 Swift 5.1 / Xcode 11.0

原始答案

虽然它有点 hacky,但您可以在延迟后将start = true动画设置为任何设置。.spring()

struct WelcomeView: View {

    @State var start: Bool = false
    @State var animation: Animation = Animation.easeOut(duration: 0.5).delay(0.4)

    var body: some View {
        VStack {
            // This is just here to toggle start variable
            Button("Toggle", action: {
                if !self.start {
                    // Show button, and after combined delay + duration, set animation to .spring()
                    self.start.toggle()
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) {
                        self.animation = .spring()
                    }
                } else {
                    // Set animation to .easeOut(), then hide button
                    self.animation = Animation.easeOut(duration: 0.5).delay(0.4)
                    self.start.toggle()
                }
            })

            GeometryReader { g in
                Button(action: {}) {
                    Text("New Account")
                }
                .buttonStyle(fillCircular(width: g.size.width - (24 * 2)))
                .offset(y: self.start ? 0 : 20)
                .opacity(self.start ? 1 : 0)
                .animation(self.animation)
            }
        }
    }
}

推荐阅读