如何在动画期间使按钮处于非活动状态

时间:2021-07-15 14:47:56

标签: ios swift swiftui

我想在动画完成之前停用按钮。 我希望它在动画结束时处于活动状态。

我写了以下内容,但没有奏效。 它立即激活。

  <%= @post.elements.each do |element|%>
    <% if element.persisted?%>
        <%= form_with(model: [@post, element]) do |form|%>
        <%= form.text_area :content %>
        <%= form.submit "save", class: 'btn btn-primary'%>
      <%end%> 
    <%end%>

3 个答案:

答案 0 :(得分:4)

SwiftUI 动画没有完成处理程序,但您可以监视可动画属性的状态并监听其更改。这可以满足您的需求,并且与动画的时间无关

SwiftUI 具有 AnimatableModifier,您可以使用它来创建在动画完成时调用函数的修饰符。

您可以在 Except

看到对此的解释
struct ContentView: View {
    @State private var scale: CGFloat = 1
    @State private var isDisable = false

    var body: some View {
        VStack {
            Button(
                action: {
                    self.isDisable = true
                    withAnimation(
                        .linear(duration: 1)
                    ) {
                        scale = scale - 0.1
                    }
                },
                label: {
                    Text("Tap Me")
                }
            )
            .disabled(
                isDisable
            )
            RectangleView()
                .scaleEffect(scale)
                .onAnimationCompleted(for: scale) {
                    isDisable = false
            }
        }
    }
}

struct RectangleView: View {
    var body: some View {
        Rectangle().fill(
            Color.blue
        )
        .frame(
            width:200,
            height: 150
        )
    }
}

/// An animatable modifier that is used for observing animations for a given animatable value.
struct AnimationCompletionObserverModifier<Value>: AnimatableModifier where Value: VectorArithmetic {

    /// While animating, SwiftUI changes the old input value to the new target value using this property. This value is set to the old value until the animation completes.
    var animatableData: Value {
        didSet {
            notifyCompletionIfFinished()
        }
    }

    /// The target value for which we're observing. This value is directly set once the animation starts. During animation, `animatableData` will hold the oldValue and is only updated to the target value once the animation completes.
    private var targetValue: Value

    /// The completion callback which is called once the animation completes.
    private var completion: () -> Void

    init(observedValue: Value, completion: @escaping () -> Void) {
        self.completion = completion
        self.animatableData = observedValue
        targetValue = observedValue
    }

    /// Verifies whether the current animation is finished and calls the completion callback if true.
    private func notifyCompletionIfFinished() {
        guard animatableData == targetValue else { return }

        /// Dispatching is needed to take the next runloop for the completion callback.
        /// This prevents errors like "Modifying state during view update, this will cause undefined behavior."
        DispatchQueue.main.async {
            self.completion()
        }
    }

    func body(content: Content) -> some View {
        /// We're not really modifying the view so we can directly return the original input value.
        return content
    }
}

extension View {

    /// Calls the completion handler whenever an animation on the given value completes.
    /// - Parameters:
    ///   - value: The value to observe for animations.
    ///   - completion: The completion callback to call once the animation completes.
    /// - Returns: A modified `View` instance with the observer attached.
    func onAnimationCompleted<Value: VectorArithmetic>(for value: Value, completion: @escaping () -> Void) -> ModifiedContent<Self, AnimationCompletionObserverModifier<Value>> {
        return modifier(AnimationCompletionObserverModifier(observedValue: value, completion: completion))
    }
}

答案 1 :(得分:2)

添加延迟。

{
    isDisable = true
    withAnimation(
        .linear(duration: 1)
    ) {
        scale = scale - 0.1
    }
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) { //< Here
        isDisable = false
    }
}

答案 2 :(得分:1)

Button {
    let duration: Double = 1
    isDisable = true
    DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
        isDisable = false
    }
    withAnimation(.linear(duration: duration)) {
        scale -= 0.1
    }
} label: {
    Text("Tap Me")
}