在SwiftUI中连续重复执行动作

11
如何使元素(如文本框)持续缩放?
我有以下代码:
struct ContentView : View {
    @State var size:Double = 0.5

    var body: some View { 
        ZStack {
            Text("Hello!")
                 .padding()
                 .scaleEffect(size)
        }
    }
}

我知道需要在某种循环中增加大小然后再减小它,但以下内容无法在SwiftUI中完成:

while true {

  self.size += 0.8
  sleep(0.2)
  self.size -= 0.8

}
4个回答

21

一种可能的解决方案是使用(重复,自动反转)动画:

struct ContentView : View {
    @State var size: CGFloat = 0.5
    
    var repeatingAnimation: Animation {
        Animation
            .easeInOut(duration: 2) //.easeIn, .easyOut, .linear, etc...
            .repeatForever()
    }

    var body: some View {
        Text("Hello!")
            .padding()
            .scaleEffect(size)
            .onAppear() {
                withAnimation(self.repeatingAnimation) { self.size = 1.3 }
        }
    }
}

11

9

最好的方式是创建单独的动画结构体并配置所有需要的选项(这样您的代码将更加紧凑)。

为了让它更清晰和逻辑性,使用@State属性isAnimating。您将能够停止您的动画并再次恢复,并了解何时它正在进行中。

    @State private var isAnimating = false

    var foreverAnimation: Animation {
        Animation.linear(duration: 0.3)
        .repeatForever()
    }

    var body: some View {

        Text("Hello")
            .scaleEffect(isAnimating ? 1.5 : 1)
            .animation(foreverAnimation)
            .onAppear {
                self.isAnimating = true
        }
}

使用计算属性没有意义。你也没有创建“单独的动画结构体”。 - Peter Schorn

4
在 if 语句内部使用重复动画的视图会出现奇怪的行为。如果你想实现这个功能:
if something {
    BlinkingView()
}

使用带有动画修饰符的转换,否则视图将留在屏幕上,即使 something 被设置为 false。

我创建了这个扩展来显示一个视图,该视图从一种状态变化到另一种状态并返回:

extension AnyTransition {
    static func repeating<T: ViewModifier>(from: T, to: T, duration: Double = 1) -> AnyTransition {
       .asymmetric(
            insertion: AnyTransition
                .modifier(active: from, identity: to)
                .animation(Animation.easeInOut(duration: duration).repeatForever())
                .combined(with: .opacity), 
            removal: .opacity
        )
    }
}

这使得视图可以通过AnyTransition.opacity实现出现和消失,当它显示时,它会在延迟duration的情况下在fromto状态之间切换。

示例用法:

struct Opacity: ViewModifier {
    private let opacity: Double
    init(_ opacity: Double) {
        self.opacity = opacity
    }

    func body(content: Content) -> some View {
        content.opacity(opacity)
    }
}

struct ContentView: View {
    @State var showBlinkingView: Bool = false

    var body: some View {
        VStack {
            if showBlinkingView {
                Text("I am blinking")
                    .transition(.repeating(from: Opacity(0.3), to: Opacity(0.7)))
            }
            Spacer()
            Button(action: {
                self.showBlinkingView.toggle()
            }, label: {
                Text("Toggle blinking view")
            })
        }.padding(.vertical, 50)
    }
}

编辑:

当显示条件为真时,过渡不会开始。为了解决这个问题,在父视图(例如我的示例中的VStack)出现时,我切换该条件:

.onAppear {
    if self.showBlinkingView {
        self.showBlinkingView.toggle()
        DispatchQueue.main.async {
            self.showBlinkingView.toggle()
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接