SwiftUI - 使用淡入淡出动画更改文本 [英] SwiftUI - Change Text with fade animation

查看:69
本文介绍了SwiftUI - 使用淡入淡出动画更改文本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想看看如何通过淡入淡出来更改文本视图的源代码.

因此,如果我有一个如下所示的简单视图:

struct TestView: View {@State var text =我是灌木."功能更改文本(){text = (text == "我是灌木") ?我的名字是灌木罗杰.":我是灌木."}var主体:一些视图{虚拟堆栈{文本(self.text)测试按钮(标签:更改",操作:更改文本)}}}

如果我点击按钮,文本会立即改变.

现在,如果我将更改代码包装在 withAnimation 块中,或者向 Text 视图添加一个 .animation 视图修饰符,它只会对帧中的更改进行动画处理尺寸.这不是我要找的:

struct TestView: View {@State var text =我是灌木."功能更改文本(){带动画{text = (text == "我是灌木") ?我的名字是灌木罗杰.":我是灌木."}}var主体:一些视图{虚拟堆栈{文本(self.text)测试按钮(标签:更改",操作:更改文本)}}}

当然,我可以通过使用 .transition 视图修饰符和单独的文本字段来接近我正在寻找的效果(这个答案:

但这是一种糟糕的模式,并且不适用于任意文本更改.即便如此,淡入淡出也会同时发生.我想让淡出/淡入顺序发生.

解决方案

好吧,我想我已经找到了自己处理这个问题的最佳方法:通过绑定的发布者观察更改,然后手动淡出,更改文本,并逐渐消退.

视图如下所示:

struct FadingTextView: View {@Binding 变量源:字符串var transitionTime: 双倍@State 私有变量 currentText:字符串?= 零@State 私有变量可见:Bool = false私有变量发布者:AnyPublisher<[String.Element], Never>{来源.出版商.搜集().eraseToAnyPublisher()}init(text: Binding, totalTransitionTime: Double) {self._source = 文本self.transitionTime = totalTransitionTime/3}私人功能更新(_:任何){守卫 currentText != nil else {当前文本 = 来源DispatchQueue.main.asyncAfter(deadline: .now() + (transitionTime)) {self.visible = 真}返回}保护源 != currentText else { return }self.visible = falseDispatchQueue.main.asyncAfter(deadline: .now() + (transitionTime)) {self.currentText = 源DispatchQueue.main.asyncAfter(deadline: .now() + (transitionTime)) {self.visible = 真}}}var主体:一些视图{文本(当前文本??").opacity(可见?1:0).animation(.linear(duration: transitionTime)).onReceive(publisher, perform: update(_:))}}

然后它可以用于像这样的任意文本更改:

struct TestView: View {@State private var text: String = "Alpha";私有变量值:[String] = [Alpha"、Bravo"、Charlie"、Delta"、Echo"、Foxtrot"、Golf"、Hotel"]私人功能更新文本(){self.text = values.randomElement()!}var主体:一些视图{虚拟堆栈{FadingTextView(text: $text, totalTransitionTime: 1.0)按钮(更改",操作:updateText).padding()}}}

看起来像这样:

I'd like to see how I can make it so changes to a Text view's source change with a fade in and out.

So if I've got a simple view like the following:

struct TestView: View {
    
    @State var text = "I am a shrubber."
    
    func changeText() {
        text = (text == "I am a shrubber.") ? "My name is Roger the Shrubber." : "I am a shrubber."
    }
    
    var body: some View {
        VStack {
            Text(self.text)
            TestButton(label: "Change", action: changeText)
        }
    }
    
}

If I tap the button the text changes instantly.

Now, if I wrap the changing code in a withAnimation block, or add an .animation view modifier to the Text view, it just animates the change in the frame size. Which isn't what I'm looking for:

struct TestView: View {
    
    @State var text = "I am a shrubber."
    
    func changeText() {
        withAnimation {
            text = (text == "I am a shrubber.") ? "My name is Roger the Shrubber." : "I am a shrubber."
        }
    }
    
    var body: some View {
        VStack {
            Text(self.text)
            TestButton(label: "Change", action: changeText)
        }
    }
    
}

Of course, I could get close to the effect I'm looking for by using .transition view modifiers and separate text fields (this answer: https://stackoverflow.com/a/60984127/5919644), like this:

struct TestView: View {
    
    @State var changed = false
    
    var body: some View {
        VStack {
            if !changed {
                Text("I am a shrubber.")
                    .transition(AnyTransition.opacity.animation(.easeInOut(duration:0.3)))
            }
            if changed {
                Text("My name is Roger the Shrubber.")
                    .transition(AnyTransition.opacity.animation(.easeInOut(duration:0.3)))
            }
            
            TestButton(label: "Change", action: { self.changed.toggle() })
        }
    }
    
}

But that is terrible pattern and doesn't work with arbitrary text changes. And even then the fade in and out happen at the same time. I'd like to have the fade out/in happen sequentially.

解决方案

Okay, so I think I've found the best way to handle this myself: watching for changes via a binding's publisher, then manually fading out, changing text, and fading back.

The view looks like this:

struct FadingTextView: View {
    
    @Binding var source: String
    var transitionTime: Double
    
    @State private var currentText: String? = nil
    @State private var visible: Bool = false
    private var publisher: AnyPublisher<[String.Element], Never> {
        source
            .publisher
            .collect()
            .eraseToAnyPublisher()
    }
    
    init(text: Binding<String>, totalTransitionTime: Double) {
        self._source = text
        self.transitionTime = totalTransitionTime / 3
    }
    
    private func update(_: Any) {
        guard currentText != nil else {
            currentText = source
            DispatchQueue.main.asyncAfter(deadline: .now() + (transitionTime)) {
                self.visible = true
            }
            return
        }
        guard source != currentText else { return }
        self.visible = false
        DispatchQueue.main.asyncAfter(deadline: .now() + (transitionTime)) {
            self.currentText = source
            DispatchQueue.main.asyncAfter(deadline: .now() + (transitionTime)) {
                self.visible = true
            }
        }
    }
    
    var body: some View {
        Text(currentText ?? "")
            .opacity(visible ? 1 : 0)
            .animation(.linear(duration: transitionTime))
            .onReceive(publisher, perform: update(_:))
    }
    
}

Then it can be used for arbitrary text changes like this:

struct TestView: View {
    
    @State private var text: String = "Alpha"
    
    private var values: [String] = ["Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel"]
    
    private func updateText() {
        self.text = values.randomElement()!
    }
    
    var body: some View {
        VStack {
            FadingTextView(text: $text, totalTransitionTime: 1.0)
            Button("Change", action: updateText).padding()
        }
    }
}

And looks like this:

这篇关于SwiftUI - 使用淡入淡出动画更改文本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆