Search code examples
iosswiftiphoneswiftui

SwiftUI: Floating Textfield placeholder is not animate as expected


I really need your help with this. The placeholder of the password field does not animate until I start typing something. However, the email address label animation works fine as expected. When I tap on email address field, its label animates correctly. Additionally, could you please review my code to check for any technical issues, as I am currently learning SwiftUI.

I greatly appreciate your help.

Thanks in advance.

MyCode:

//FloatingTextFieldView.swift
import SwiftUI

@available(iOS 15.0, *)
struct FloatingTextFieldView: View {
    var type: String
    var placeholder: String
    var rightIcon: String?
    var leftIcon: String?
    
    @Binding var text: String
    var onTap: () -> Void = {}
    
    private enum Field: Int, Hashable {
        case fieldName
    }
    
    @State private var isActive: Bool = false
    
    @FocusState private var focusField: Field?

    var body: some View {
        VStack(alignment: .leading) {
            ZStack(alignment: .leading) {
                HStack {
                    if let leftIcon = leftIcon {
                        ImageView(name: rightIcon)
                            .onTapGesture {
                                print("----Icon Tapped with \(text)")
                            }
                    }
                    if type == "password" {
                        SecureField("", text: $text, onCommit: {

                        })
                        .padding(.leading, leftIcon == nil ? 0 : 8)
                        .padding(.trailing, rightIcon == nil ? 0 : 8)
                        .focused($focusField, equals:.fieldName)
                    } else {
                        TextField("", text: $text, onEditingChanged: { editing in
                            self.isActive = editing
                            print("----FieldActive \(self.isActive)")
                        }) {

                        }
                        .padding(.leading, leftIcon == nil ? 0 : 8)
                        .padding(.trailing, rightIcon == nil ? 0 : 8)
                        .focused($focusField, equals:.fieldName)
                    }
                    if let rightIcon = rightIcon {
                        ImageView(name: rightIcon)
                            .onTapGesture {
                                print("----Icon Tapped with \(text)")
                            }
                    }
                }
                .padding()
                .overlay(
                    RoundedRectangle(cornerRadius: 5)
                        .stroke(Color.gray, lineWidth: 1)
                )
                
                CutomTextView(placeholder: placeholder,
                              color: .red,
                              backgroundColor: .white,
                              callback: {},
                              isActive: (!self.isActive && self.text.isEmpty)
                )
            }
        }
        .padding()
    }
}


//CustomTextView.swift
import SwiftUI

struct CutomTextView: View {
    
    var placeholder: String? = ""
    var color: Color
    var backgroundColor: Color
    var callback: ()->Void
    var isActive: Bool
    
    var body: some View {
        
        Text(placeholder ?? "placeholder")
            .foregroundColor(color)
            .padding(.horizontal, 10)
            .background(backgroundColor)
            .offset(x: self.isActive ? 20 : 10  ,y: self.isActive ? 0 : -35 )
            .scaleEffect(self.isActive ? 1 : 0.8, anchor: .leading)
            .animation(.easeInOut(duration: 0.4), value: self.isActive)
            .onTapGesture {
            }
        
    }
}

Currently issue is the password field placeholder does not animate when I tap on the field, but it does animate when I start typing. I what it to animate when I tap on the field same as email address field placeholder.


Solution

  • You already have a focus state variable that is detecting when the field receives focus, so I would suggest you use this to determine when the field is active.

    Also, the view CustomTextView uses the flag isActive in reverse, because when isActive is true, the placeholder is occupying the full field. So you might want to switch the logic in CustomTextView. But based on the way the logic is currently implemented, try making these changes to FloatingTextFieldView:

    • Change the value of isActive that is passed to CustomTextView:
    // isActive: (!self.isActive && self.text.isEmpty)
    isActive: focusField != .fieldName && text.isEmpty
    
    • The state variable isActive is redundant, it can be deleted.