如何在SwiftUI中动画化/转换文本值更改

时间:2019-12-03 12:53:41

标签: ios swift animation swiftui

我正在尝试使用withAnimation为文本中的值更改设置动画,但是它似乎不起作用。我遇到了类似的问题,但是answer并未将文本值设置为动画。

我正在尝试在纯SwiftUI中重新创建此行为 (UIKit Example):

enter image description here

我已经尝试过这段代码,但是它没有使文本更改动起来:

struct TextAnimationView: View {
    @State private var textValue = "0"
    var body: some View {
        VStack (spacing: 50) {
            Text(textValue)
                .font(.largeTitle)
                .frame(width: 200, height: 200)
                .transition(.opacity)
            Button("Next") {
                withAnimation (.easeInOut(duration: 1)) {
                    self.textValue = "\(Int.random(in: 1...100))"
                }
            }
        }
    }
}

我对SwiftUI的经验很少,还有另一种方法可以实现这一目标吗?

预先感谢:)

4 个答案:

答案 0 :(得分:29)

所以事实证明这真的很简单

Text(textValue)
  .font(.largeTitle)
  .frame(width: 200, height: 200)
  .transition(.opacity)
  .id("MyTitleComponent" + textValue)

请注意最后还有一个附加的id。 SwiftUI使用它来决定在重绘时是否正在处理相同的视图。如果id不同,则假定已删除上一个视图,并且已添加了该视图。因为它正在添加新视图,所以将按预期应用指定的过渡。

注意:这个ID在整个视图树中很可能是唯一的,因此您可能需要注意为其命名空间(因此在示例中为MyTitleComponent前缀)。

答案 1 :(得分:1)

我找不到一种淡化文本值动画的方法。设置Text的动画属性时,在设置动画时会看到三个点(...)。

到目前为止,我已经找到一种可以改变不透明度的方法:

@State private var textValue: Int = 1
@State private var opacity: Double = 1

var body: some View {
    VStack (spacing: 50) {
        Text("\(textValue)")
            .font(.largeTitle)
            .frame(width: 200, height: 200)
            .opacity(opacity)
        Button("Next") {
            withAnimation(.easeInOut(duration: 0.5), {
                self.opacity = 0
            })
            self.textValue += 1
            withAnimation(.easeInOut(duration: 1), {
                self.opacity = 1
            })
        }
    }
}

当您更改它时,它会淡出并淡入文本。

答案 2 :(得分:1)

这是使用标准过渡的方法。字体大小,框架,动画持续时间可以根据您的需要进行配置。演示仅包含重要的方法。

SwiftUI fade transition

public class Foo {

    interface Bar {
    }

    void doesNotCompile() {
        Optional.<Bar>of(new Bar() {
        }).orElse(new Bar() {
        });
    }

    void doesNotCompile2() {
        final Bar bar = new Bar() {
        };
        Optional.<Bar>of(new Bar() {
        }).orElse(bar);
    }

    void compiles1() {
        final Bar bar = new Bar() {
        };
        Optional.of(bar).orElse(new Bar() {
        });
    }
}

答案 3 :(得分:0)

以下是使用AnimatableModifier的方法。它只会淡入新值。如果您也想淡出旧值,则自定义修饰符并不难。另外,由于您的显示值为数字,因此您可以对其进行一些较小的修改就可以将其本身用作控制变量。

这种方法不仅可以用于淡入淡出,还可以用于其他类型的动画,以响应视图值的更改。您可以将其他参数传递给修饰符。您也可以完全忽略content中传递的body,并创建并返回一个全新的视图。在这种情况下,OverlayEmptyView等也很方便。

import SwiftUI

struct FadeModifier: AnimatableModifier {
    // To trigger the animation as well as to hold its final state
    private let control: Bool

    // SwiftUI gradually varies it from old value to the new value
    var animatableData: Double = 0.0

    // Re-created every time the control argument changes
    init(control: Bool) {
        // Set control to the new value
        self.control = control

        // Set animatableData to the new value. But SwiftUI again directly
        // and gradually varies it from 0 to 1 or 1 to 0, while the body
        // is being called to animate. Following line serves the purpose of
        // associating the extenal control argument with the animatableData.
        self.animatableData = control ? 1.0 : 0.0
    }

    // Called after each gradual change in animatableData to allow the
    // modifier to animate
    func body(content: Content) -> some View {
        // content is the view on which .modifier is applied
        content
            // Map each "0 to 1" and "1 to 0" change to a "0 to 1" change
            .opacity(control ? animatableData : 1.0 - animatableData)

            // This modifier is animating the opacity by gradually setting
            // incremental values. We don't want the system also to
            // implicitly animate it each time we set it. It will also cancel
            // out other implicit animations now present on the content.
            .animation(nil)
    }
}

struct ExampleView: View {
    // Dummy control to trigger animation
    @State var control: Bool = false

    // Actual display value
    @State var message: String = "Hi" {
        didSet {
            // Toggle the control to trigger a new fade animation
            control.toggle()
        }
    }

    var body: some View {
        VStack {
            Spacer()

            Text(message)
                .font(.largeTitle)

                // Toggling the control causes the re-creation of FadeModifier()
                // It is followed by a system managed gradual change in the
                // animatableData from old value of control to new value. With
                // each change in animatableData, the body() of FadeModifier is
                // called, thus giving the effect of animation
                .modifier(FadeModifier(control: control))

                // Duration of the fade animation
                .animation(.easeInOut(duration: 1.0))

            Spacer()

            Button(action: {
                self.message = self.message == "Hi" ? "Hello" : "Hi"
            }) {
                Text("Change Text")
            }

            Spacer()
        }
    }
}

struct ExampleView_Previews: PreviewProvider {
    static var previews: some View {
        ExampleView()
    }
}