A highly customizable and feature-rich floating label text field component for SwiftUI, inspired by Material Design principles.
- π¨ Multiple Built-in Styles: Default, Material, Outlined, Filled, Minimal, and Custom styles
 - β Comprehensive Validation: Built-in validators for email, phone, URL, password, and more
 - π Real-time Validation: Validate as users type with customizable error messages
 - π Secure Text Entry: Built-in password field support with show/hide toggle
 - π― Focus Management: Smooth animations and focus state handling
 - π§ Highly Customizable: Extensive modifier API for complete customization
 - π± Keyboard Support: Different keyboard types and input methods
 - πΌοΈ Icon Support: Leading and trailing icons with SF Symbols
 - π‘ Helper Text: Optional helper text and validation error messages
 - π Custom Content: Support for custom leading and trailing content
 
- iOS 15.0+
 - Xcode 16.0+
 - Swift 5.5+
 
Add the following to your Package.swift file:
dependencies: [
    .package(url: "https://github.com/mohammadnabulsi/FloatingTextField.git", from: "1.0.0")
]Or add it through Xcode:
- Go to 
FileβAdd Package Dependencies - Enter the repository URL
 - Click 
Add Package 
import SwiftUI
import FloatingTextFieldSwiftUI
struct ContentView: View {
    @State private var email = ""
    @State private var name = ""
    
    var body: some View {
        VStack(spacing: 20) {
            FloatingTextField($name, label: "Full Name")
                .leadingIcon("person")
            
            FloatingTextField($email, label: "Email Address")
                .leadingIcon("envelope")
                .keyboardType(.emailAddress)
                .email()
                .required()
        }
        .padding()
    }
}struct PasswordView: View {
    @State private var password = ""
    @State private var isPasswordVisible = false
    
    var body: some View {
        FloatingTextField($password, label: "Password")
            .leadingIcon("lock")
            .secure(!isPasswordVisible)
            .password(minLength: 8, requireNumbers: true, requireSpecialChars: true)
            .trailingContent {
                Button(action: { isPasswordVisible.toggle() }) {
                    Image(systemName: isPasswordVisible ? "eye.slash" : "eye")
                        .foregroundColor(.secondary)
                }
            }
            .padding()
    }
}// Default Style
FloatingTextField($text, label: "Default")
// Material Design Style
FloatingTextField($text, label: "Material")
    .floatingTextFieldStyle(MaterialFloatingStyle())
// Outlined Style
FloatingTextField($text, label: "Outlined")
    .floatingTextFieldStyle(OutlinedFloatingStyle())
// Filled Style
FloatingTextField($text, label: "Filled")
    .floatingTextFieldStyle(FilledFloatingStyle())
// Minimal Style
FloatingTextField($text, label: "Minimal")
    .floatingTextFieldStyle(MinimalFloatingStyle())
// Custom Style
FloatingTextField($text, label: "Custom")
    .floatingTextFieldStyle(CustomFloatingStyle(
        accent: .purple,
        background: .purple.opacity(0.1),
        border: .purple.opacity(0.3)
    ))struct ValidationExample: View {
    @State private var email = ""
    @State private var phone = ""
    @State private var username = ""
    @State private var age = ""
    
    var body: some View {
        VStack(spacing: 16) {
            // Email validation
            FloatingTextField($email, label: "Email")
                .leadingIcon("envelope")
                .email("Please enter a valid email address")
                .required("Email is required")
                .validateRealTime()
            
            // Phone number validation
            FloatingTextField($phone, label: "Phone Number")
                .leadingIcon("phone")
                .phoneNumber("Please enter a valid phone number")
                .keyboardType(.phonePad)
            
            // Username validation
            FloatingTextField($username, label: "Username")
                .leadingIcon("at")
                .alphanumeric("Only letters and numbers allowed")
                .minLength(3, errorMessage: "Username must be at least 3 characters")
                .maxLength(20, errorMessage: "Username cannot exceed 20 characters")
            
            // Age validation
            FloatingTextField($age, label: "Age")
                .leadingIcon("calendar")
                .numeric("Please enter a valid number")
                .keyboardType(.numberPad)
        }
        .padding()
    }
}FloatingTextField($text, label: "Custom Validation")
    .validation([
        ValidationRule(
            condition: { !$0.isEmpty },
            errorMessage: "This field cannot be empty"
        ),
        ValidationRule(
            condition: { $0.count >= 5 },
            errorMessage: "Must be at least 5 characters"
        ),
        ValidationRule(
            condition: { $0.contains("@") },
            errorMessage: "Must contain @ symbol"
        )
    ])
    .validateRealTime(true)
    .onValidationChange { isValid in
        print("Field is valid: \(isValid)")
    }struct RegistrationForm: View {
    @State private var fullName = ""
    @State private var email = ""
    @State private var password = ""
    @State private var confirmPassword = ""
    @State private var website = ""
    @State private var isPasswordVisible = false
    @State private var isFormValid = false
    
    var body: some View {
        ScrollView {
            VStack(spacing: 20) {
                Text("Create Account")
                    .font(.title2)
                    .fontWeight(.bold)
                
                FloatingTextField($fullName, label: "Full Name")
                    .leadingIcon("person")
                    .required("Please enter your full name")
                    .alphabetic("Name should only contain letters")
                    .minLength(2)
                    .floatingTextFieldStyle(MaterialFloatingStyle())
                    .onValidationChange { isValid in
                        updateFormValidation()
                    }
                
                FloatingTextField($email, label: "Email Address")
                    .leadingIcon("envelope")
                    .keyboardType(.emailAddress)
                    .required("Email address is required")
                    .email("Please enter a valid email format")
                    .floatingTextFieldStyle(OutlinedFloatingStyle())
                    .onValidationChange { isValid in
                        updateFormValidation()
                    }
                
                FloatingTextField($password, label: "Password")
                    .leadingIcon("lock")
                    .secure(!isPasswordVisible)
                    .password(
                        minLength: 8,
                        requireNumbers: true,
                        requireSpecialChars: true,
                        errorMessage: "Password must be 8+ characters with numbers and special characters"
                    )
                    .trailingContent {
                        Button(action: { isPasswordVisible.toggle() }) {
                            Image(systemName: isPasswordVisible ? "eye.slash" : "eye")
                                .foregroundColor(.secondary)
                        }
                    }
                    .floatingTextFieldStyle(FilledFloatingStyle())
                    .onValidationChange { isValid in
                        updateFormValidation()
                    }
                
                FloatingTextField($confirmPassword, label: "Confirm Password")
                    .leadingIcon("lock")
                    .secure(true)
                    .validation([
                        ValidationRule(
                            condition: { !$0.isEmpty },
                            errorMessage: "Please confirm your password"
                        ),
                        ValidationRule(
                            condition: { $0 == password },
                            errorMessage: "Passwords don't match"
                        )
                    ])
                    .onValidationChange { isValid in
                        updateFormValidation()
                    }
                
                FloatingTextField($website, label: "Website")
                    .leadingIcon("globe")
                    .keyboardType(.URL)
                    .url("Please enter a valid URL")
                    .helperText("Optional: Enter your website URL")
                    .floatingTextFieldStyle(MinimalFloatingStyle())
                
                Button(action: submitForm) {
                    Text("Create Account")
                        .fontWeight(.semibold)
                        .frame(maxWidth: .infinity)
                        .padding()
                        .background(isFormValid ? Color.blue : Color.gray)
                        .foregroundColor(.white)
                        .cornerRadius(12)
                }
                .disabled(!isFormValid)
            }
            .padding()
        }
    }
    
    private func updateFormValidation() {
        // Custom form validation logic
        isFormValid = !fullName.isEmpty && !email.isEmpty && !password.isEmpty && !confirmPassword.isEmpty
    }
    
    private func submitForm() {
        print("Form submitted successfully!")
    }
}| Modifier | Description | Example | 
|---|---|---|
.floatingTextFieldStyle(_:) | 
Apply a custom style | .floatingTextFieldStyle(MaterialFloatingStyle()) | 
.secure(_:) | 
Enable/disable secure text entry | .secure(true) | 
.keyboardType(_:) | 
Set keyboard type | .keyboardType(.emailAddress) | 
.leadingIcon(_:) | 
Add leading icon | .leadingIcon("envelope") | 
.trailingContent(_:) | 
Add custom trailing content | .trailingContent { Button(...) } | 
.leadingContent(_:) | 
Add custom leading content | .leadingContent { Image(...) } | 
.helperText(_:) | 
Set helper text | .helperText("Optional field") | 
.enabled(_:) | 
Enable/disable field | .enabled(false) | 
.validateRealTime(_:) | 
Enable real-time validation | .validateRealTime(true) | 
.onValidationChange(_:) | 
Validation state callback | .onValidationChange { isValid in ... } | 
| Modifier | Description | Example | 
|---|---|---|
.required(_:) | 
Make field required | .required("This field is required") | 
.email(_:) | 
Email validation | .email("Invalid email format") | 
.phoneNumber(_:) | 
Phone number validation | .phoneNumber("Invalid phone number") | 
.url(_:) | 
URL validation | .url("Invalid URL") | 
.minLength(_:errorMessage:) | 
Minimum length | .minLength(8, errorMessage: "Too short") | 
.maxLength(_:errorMessage:) | 
Maximum length | .maxLength(50, errorMessage: "Too long") | 
.alphanumeric(_:) | 
Alphanumeric only | .alphanumeric("Letters and numbers only") | 
.alphabetic(_:) | 
Alphabetic only | .alphabetic("Letters only") | 
.numeric(_:) | 
Numeric only | .numeric("Numbers only") | 
.password(...) | 
Password validation | .password(minLength: 8, requireNumbers: true) | 
.validation(_:) | 
Custom validation rules | .validation([ValidationRule(...)]) | 
This project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by Material Design floating label text fields
 - Built with β€οΈ for the SwiftUI community
 
If you have any questions or need help, please open an issue on GitHub.
β Star this repo if you found it helpful! β
