【问题标题】:SwiftUI: Animate Text color - foregroundColor()SwiftUI:动画文本颜色 - foregroundColor()
【发布时间】:2020-01-09 22:44:07
【问题描述】:

我一直在尝试为 UI 的各个部分制作动画,但您似乎无法为 SwiftUI 文本的前景颜色制作动画?我想在状态改变时平滑地切换一些文本的颜色。如果我为文本周围视图的背景颜色设置动画效果,这很好用,但前景色不起作用。有没有人有幸为这样的变化制作动画?不确定这是 Xcode 测试版错误还是预期的功能...

Text(highlightedText)
    .foregroundColor(color.wrappedValue)
    .animation(.easeInOut)

// Error: Cannot convert value of type 'Text' to closure result type '_'

【问题讨论】:

  • 快速提问?您可以在UIKit 中执行此操作吗? (老实说,我不知道。)我猜您在 SwiftUI 中可以做的事情很少,而目前在 UIKit 中是做不到的。
  • @dfd 快速回答。是的,你可以。
  • @MojtabaHosseini,然后我会说发布 UIKit 代码并让 OP 将其转换为 UIViewRepresentable。
  • @dfd 为了反对者,我提出了一个不使用UIKit的解决方案。
  • 我在 reddit 上找到了这个答案,它为我解决了这个问题:reddit.com/r/SwiftUI/comments/hxbdns/… 基本上,添加这个: .foregroundColor(.clear) .overlay(Rectangle() .foregroundColor(color.wrappedValue) .mask(文本(highlightedText)))

标签: ios swift swiftui


【解决方案1】:

有一个更简单的方法,借鉴 Apple 的“动画视图和过渡”教程代码。 HikeGraph 中 GraphCapsule 的实例化证明了这一点。

虽然foregroundColor 不能动画,但colorMultiply 可以。将前景色设置为白色并使用 colorMultiply 设置您想要的实际颜色。从红色到蓝色的动画:

struct AnimateDemo: View {
    @State private var color = Color.red

    var body: some View {
        Text("Animate Me!")
            .foregroundColor(Color.white)
            .colorMultiply(self.color)
            .onTapGesture {
                withAnimation(.easeInOut(duration: 1)) {
                    self.color = Color.blue
                }
        }
    }
}

struct AnimateDemo_Previews: PreviewProvider {
    static var previews: some View {
        AnimateDemo()
    }
}

【讨论】:

    【解决方案2】:

    SwiftUI 中有一个很好的协议,可以让你为任何东西制作动画。即使是不可动画的东西! (如文字颜色)。该协议称为 AnimatableModifier。

    如果您想了解更多信息,我写了一篇完整的文章来解释它是如何工作的:https://swiftui-lab.com/swiftui-animations-part3/

    这是一个关于如何实现这样一个视图的示例:

    AnimatableColorText(from: UIColor.systemRed, to: UIColor.systemGreen, pct: flag ? 1 : 0) {
        Text("Hello World").font(.largeTitle)
    }.onTapGesture {
        withAnimation(.easeInOut(duration: 2.0)) {
          self.flag.toggle()
        }
    }
    

    以及实现:

    struct AnimatableColorText: View {
        let from: UIColor
        let to: UIColor
        let pct: CGFloat
        let text: () -> Text
    
        var body: some View {
            let textView = text()
    
            // This should be enough, but there is a bug, so we implement a workaround
            // AnimatableColorTextModifier(from: from, to: to, pct: pct, text: textView)
    
            // This is the workaround
            return textView.foregroundColor(Color.clear)
                .overlay(Color.clear.modifier(AnimatableColorTextModifier(from: from, to: to, pct: pct, text: textView)))
        }
    
        struct AnimatableColorTextModifier: AnimatableModifier {
            let from: UIColor
            let to: UIColor
            var pct: CGFloat
            let text: Text
    
            var animatableData: CGFloat {
                get { pct }
                set { pct = newValue }
            }
    
            func body(content: Content) -> some View {
                return text.foregroundColor(colorMixer(c1: from, c2: to, pct: pct))
            }
    
            // This is a very basic implementation of a color interpolation
            // between two values.
            func colorMixer(c1: UIColor, c2: UIColor, pct: CGFloat) -> Color {
                guard let cc1 = c1.cgColor.components else { return Color(c1) }
                guard let cc2 = c2.cgColor.components else { return Color(c1) }
    
                let r = (cc1[0] + (cc2[0] - cc1[0]) * pct)
                let g = (cc1[1] + (cc2[1] - cc1[1]) * pct)
                let b = (cc1[2] + (cc2[2] - cc1[2]) * pct)
    
                return Color(red: Double(r), green: Double(g), blue: Double(b))
            }
    
        }
    }
    

    【讨论】:

    • 试过代码 - 非常有用。我需要的是文本以蓝色开始,然后过渡到红色,停留 2 秒,然后回到蓝色。
    • @thstart 如果您需要在两个动画之间暂停,您可以使用两个单独的动画并在 DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4)) { ... } 中启动第二个动画
    【解决方案3】:

    Text 的颜色属性在 SwiftUI 或 UIKit 中不可设置动画。 但你可以像这样达到你需要的结果:

    struct ContentView: View {
    
        @State var highlighted = false
    
        var body: some View {
            VStack {
                ZStack {
                // Highlighted State
                Text("Text To Change Color")
                    .foregroundColor(.red)
                    .opacity(highlighted ? 1 : 0)
    
                // Normal State
                Text("Text To Change Color")
                    .foregroundColor(.blue)
                    .opacity(highlighted ? 0 : 1)
                }
                Button("Change") {
                    withAnimation(.easeIn) {
                        self.highlighted.toggle()
                    }
                }
            }
        }
    }
    

    您可以将此功能封装在自定义View 中,并在您喜欢的任何地方使用它。

    【讨论】:

    • 不错的答案!希望在 SwiftUI 的未来版本中,您可以直接执行此操作。
    猜你喜欢
    • 1970-01-01
    • 2012-07-14
    • 2011-11-22
    • 2016-03-26
    • 1970-01-01
    • 1970-01-01
    • 2021-01-06
    • 2012-04-02
    • 2021-09-18
    相关资源
    最近更新 更多