【问题标题】:Animating the change of colors when text field is changed using Swift UI使用 Swift UI 更改文本字段时动画颜色变化
【发布时间】:2021-11-01 13:23:28
【问题描述】:

我创建了一个 OTP 页面,如果选择了具有相应行的文本字段,则该页面会更改下划线的颜色。颜色会正确变化,但我想添加动画,线条的颜色会像动画一样缓慢变化。我附上了一个关于预期和当前行为的示例。

我尝试在动画中添加轻松,但在关闭数字和文本时出现问题。它们都不会同时下降或过渡。在录音中,当我点击完成时,台词稍微落后了一点。

附言。我只能使用 Xcode 12.3 版本。

import SwiftUI

@available(iOS 13.0, *)
class OTPViewModel: ObservableObject {
    
    @Published var otpField = "" {
        didSet {
            isNextTypedArr = Array(repeating: false, count: 6)
            guard otpField.count <= 6,
                  otpField.last?.isNumber ?? true else {
                otpField = oldValue
                return
            }
            if otpField.count < 6 {
                isNextTypedArr[otpField.count] = true
            }
        }
    }
    var otp1: String {
        guard otpField.count >= 1 else {
            return ""
        }
        return String(Array(otpField)[0])
    }
    var otp2: String {
        guard otpField.count >= 2 else {
            return ""
        }
        return String(Array(otpField)[1])
    }
    var otp3: String {
        guard otpField.count >= 3 else {
            return ""
        }
        return String(Array(otpField)[2])
    }
    var otp4: String {
        guard otpField.count >= 4 else {
            return ""
        }
        return String(Array(otpField)[3])
    }
    
    var otp5: String {
        guard otpField.count >= 5 else {
            return ""
        }
        return String(Array(otpField)[4])
    }
    
    var otp6: String {
        guard otpField.count >= 6 else {
            return ""
        }
        return String(Array(otpField)[5])
    }
    
    @Published var isNextTypedArr = Array(repeating: false, count: 6)
    
    @Published var borderColor: Color = .black
    
    @Published var isEditing = false {
        didSet {
            isNextTypedArr = Array(repeating: false, count: 6)
            if isEditing && otpField.count < 6 {
                isNextTypedArr[otpField.count] = true
            }
        }
    }
}

@available(iOS 13.0, *)
struct CXLRPOTPView: View {
    
    @ObservedObject var viewModel = OTPViewModel()
    
    let textBoxWidth = UIScreen.main.bounds.width / 8
    let textBoxHeight = UIScreen.main.bounds.width / 8
    let spaceBetweenLines: CGFloat = 16
    let paddingOfBox: CGFloat = 1
    var textFieldOriginalWidth: CGFloat {
        (textBoxWidth*6)+(spaceBetweenLines*3)+((paddingOfBox*2)*3)
    }
    
    var body: some View {
        VStack {
            ZStack {
                HStack (spacing: spaceBetweenLines){
                    otpText(text: viewModel.otp1, isNextTyped: $viewModel.isNextTypedArr[0])
                    otpText(text: viewModel.otp2, isNextTyped: $viewModel.isNextTypedArr[1])
                    otpText(text: viewModel.otp3, isNextTyped: $viewModel.isNextTypedArr[2])
                    otpText(text: viewModel.otp4, isNextTyped: $viewModel.isNextTypedArr[3])
                    otpText(text: viewModel.otp5, isNextTyped: $viewModel.isNextTypedArr[4])
                    otpText(text: viewModel.otp6, isNextTyped: $viewModel.isNextTypedArr[5])
                }
                
                TextField("", text: $viewModel.otpField) { isEditing in
                    viewModel.isEditing = isEditing
                }
                .frame(width: viewModel.isEditing ? 0 : textFieldOriginalWidth, height: textBoxHeight)
                .textContentType(.oneTimeCode)
                .foregroundColor(.clear)
                .accentColor(.clear)
                .background(Color.clear)
                .keyboardType(.numberPad)
            }
        }
    }
    
    @available(iOS 13.0, *)
    private func otpText(text: String, isNextTyped: Binding<Bool>) -> some View {
        return Text(text)
            .font(Font.custom("GTWalsheim-Regular", size: 34))
            .frame(width: textBoxWidth, height: textBoxHeight)
            .background(VStack{
                Spacer()
                RoundedRectangle(cornerRadius: 1)
                    .frame(height: 2)
                    .foregroundColor(isNextTyped.wrappedValue ? Color(hex: "#367878") : Color(hex: "#BCBEC0"))
                    .animation(.easeIn) //this made the text bounced.
            })
            .padding(paddingOfBox)
    }
}

【问题讨论】:

    标签: ios swift animation swiftui colors


    【解决方案1】:

    问题在于您实际上并没有抵消颜色,因此默认情况下它只会使颜色褪色。我将绿色下划线作为一个单独的部分,根据光标当前的位置进行偏移。

    要解决动画问题,即使在做一些不相关的事情时也会产生动画 - 请改用 .animation(_:value:) - 提供一个值,动画在该值更改时发生。

    otpText 方法更改为:

    @available(iOS 13.0, *)
    private func otpText(text: String, isEditing: Bool, beforeCursor: Bool, afterCursor: Bool) -> some View {
        return Text(text)
            .font(Font.custom("GTWalsheim-Regular", size: 34))
            .frame(width: textBoxWidth, height: textBoxHeight)
            .background(VStack{
                Spacer()
    
                ZStack {
                    Capsule()
                        .frame(width: textBoxWidth, height: 2)
                        .foregroundColor(Color(hex: "#BCBEC0"))
    
                    Capsule()
                        .frame(width: textBoxWidth, height: 2)
                        .foregroundColor(Color(hex: "#367878"))
                        .offset(x: (beforeCursor ? textBoxWidth : 0) + (afterCursor ? -textBoxWidth : 0))
                        .animation(.easeInOut, value: [beforeCursor, afterCursor])
                        .opacity(isEditing ? 1 : 0)
                }
                .clipped()
            })
            .padding(paddingOfBox)
    }
    

    以及你在哪里使用这个函数(尽管很混乱,你可以用一个循环和一个数组来清理你的代码,而不是 otp1otp2 等):

    HStack (spacing: spaceBetweenLines){
        otpText(text: viewModel.otp1, isEditing: viewModel.isEditing, beforeCursor: 0 < viewModel.otpField.count, afterCursor: false)
        otpText(text: viewModel.otp2, isEditing: viewModel.isEditing, beforeCursor: 1 < viewModel.otpField.count, afterCursor: viewModel.otpField.count < 1)
        otpText(text: viewModel.otp3, isEditing: viewModel.isEditing, beforeCursor: 2 < viewModel.otpField.count, afterCursor: viewModel.otpField.count < 2)
        otpText(text: viewModel.otp4, isEditing: viewModel.isEditing, beforeCursor: 3 < viewModel.otpField.count, afterCursor: viewModel.otpField.count < 3)
        otpText(text: viewModel.otp5, isEditing: viewModel.isEditing, beforeCursor: 4 < viewModel.otpField.count, afterCursor: viewModel.otpField.count < 4)
        otpText(text: viewModel.otp6, isEditing: viewModel.isEditing, beforeCursor: false, afterCursor: viewModel.otpField.count < 5)
    }
    

    结果:


    要清理您的代码,请删除所有 otp1otp2 等并替换为以下代码:

    func otp(digit: Int) -> String {
        guard otpField.count >= digit else {
            return ""
        }
        return String(Array(otpField)[digit - 1])
    }
    

    然后可以简化视图代码:

    HStack (spacing: spaceBetweenLines){
        ForEach(1 ... 6, id: \.self) { digit in
            otpText(
                text: viewModel.otp(digit: digit),
                isEditing: viewModel.isEditing,
                beforeCursor: digit - 1 < viewModel.otpField.count,
                afterCursor: viewModel.otpField.count < digit - 1
            )
        }
    }
    

    【讨论】:

    • 嗨@George,这非常接近,但下划线只有在被选中时才会变为绿色。默认情况下,您的实现会将第一行变为绿色。在选择文本之前,预计应该只有灰线。有可能解决这个问题吗?
    • @MyronDulay 已修复!查看修改历史记录。
    • 现在工作。谢谢乔治!
    • @MyronDulay 太好了!刚刚还在答案末尾添加了一点,以向您展示如何简化代码。
    • 嗨,George,它现在可以工作了,对不起,我在添加该功能方面做得不好。我在 swift UI 中真的很新
    【解决方案2】:

    问题是您没有正确偏移颜色,因此它默认为褪色。绿色下划线是一个单独的组件,它会根据指针所在的位置而移动。

    【讨论】:

      猜你喜欢
      • 2012-04-02
      • 2014-12-12
      • 2023-03-23
      • 1970-01-01
      • 1970-01-01
      • 2010-12-27
      • 2020-09-28
      • 1970-01-01
      • 2013-05-22
      相关资源
      最近更新 更多