ios - 如何在动画期间使按钮处于非事件状态

标签 ios swift swiftui

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

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

import SwiftUI

struct ContentView: View {
  @State private var scale: CGFloat = 1
  @State private var isDisable = false
  
  var body: some View {
    VStack {
      Button(
        action: {
          isDisable = true
          withAnimation(
            .linear(duration: 1)
          ) {
            scale = scale - 0.1
            isDisable = false
          }
        },
        label: {
          Text("Tap Me")
        }
      )
      .disabled(
        isDisable
      )
      RectangleView().scaleEffect(
        scale
      )
    }
  }
}

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

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}

最佳答案

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

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

你可以在withAnimation completion callback with animatable modifiers看到这方面的解释。

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))
    }
}

关于ios - 如何在动画期间使按钮处于非事件状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68396027/

相关文章:

iphone - 导航栏上的 titleview 在 segues 后的 View 中不可见

ios - 获取节索引路径和编辑节标题

ios - 如何在没有 View Controller 的情况下阻止 UIView 旋转?

swift - 为什么在 SwiftUI 中过渡结束后应用 EdgesIgnoringSafeArea 修饰符?

iOS viewWillTransitionToSize 和设备方向

iphone - 移动到另一个 View Controller 时,UISearchBar 不会在 resignFirstResponder 上隐藏键盘

ios - widgetURL 在 Foreach 内被覆盖?

Swiftui - 添加模糊线性渐变

iphone - cocoa Facebook 登录不工作

ios - 完成处理程序未按预期与调度组和并发队列一起工作