Last active
January 24, 2025 12:01
-
-
Save Codelaby/815725f1828a2ecdd2bedc2370686a53 to your computer and use it in GitHub Desktop.
Floating Label input field by SwiftUI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import SwiftUI | |
| fileprivate extension View { | |
| @ViewBuilder func reverseMask<Mask: View>( | |
| alignment: Alignment = .center, | |
| @ViewBuilder _ mask: () -> Mask | |
| ) -> some View { | |
| self.mask { | |
| Rectangle() | |
| .overlay(alignment: alignment) { | |
| mask() | |
| .blendMode(.destinationOut) | |
| } | |
| } | |
| } | |
| } | |
| struct CustomTextField: View { | |
| @Binding var text: String | |
| let placeholder: String | |
| @FocusState var focused: Bool | |
| let fgColor = Color.primary | |
| let placeholderColor = Color.secondary | |
| let outlineNotFocused = Color.secondary.opacity(0.28) | |
| let labelColor = Color.primary | |
| var body: some View { | |
| let isFilled = text.count > 0 | |
| ZStack { | |
| TextField("", text: $text) | |
| .frame(height: 24) | |
| .focused($focused) | |
| .padding(.horizontal) | |
| .foregroundStyle(fgColor) | |
| } | |
| .frame(height: 56) //size component | |
| .background { | |
| RoundedRectangle(cornerRadius: 12) | |
| .stroke(focused ? Color.accentColor : outlineNotFocused, lineWidth: 2) | |
| .padding(2) | |
| .reverseMask(alignment: .leading) { | |
| Text(placeholder) | |
| .frame(height: 16) | |
| .font(isFilled ? .caption : .body) | |
| .background { // for label padding | |
| Rectangle() | |
| .fill() | |
| .stroke(.red, lineWidth: 8) | |
| } | |
| .padding(.leading) | |
| .offset(y: isFilled ? -28 : 0) | |
| } | |
| .overlay(alignment: .leading) { | |
| Text(placeholder) | |
| .frame(height: 16) | |
| .padding(.leading) | |
| .font(isFilled ? .caption : .body) | |
| //.font(.system(size: isFilled ? 12 : 16, weight: .regular)) | |
| .offset(y: isFilled ? -28 : 0) | |
| .foregroundStyle(isFilled ? labelColor : placeholderColor) | |
| } | |
| } | |
| .onTapGesture { | |
| focused = true | |
| } | |
| .animation(.smooth, value: isFilled) | |
| } | |
| } | |
| #Preview { | |
| struct PreviewWrapper: View { | |
| @State private var username: String = "" | |
| @State private var password: String = "" | |
| var body: some View { | |
| VStack { | |
| CustomTextField(text: $username, placeholder: "Username") | |
| CustomTextField(text: $password, placeholder: "Password") | |
| } | |
| .accentColor(.primary) // change color highlight outline | |
| .padding() | |
| } | |
| } | |
| return PreviewWrapper() | |
| .background(.brown.tertiary) | |
| //.environment(\.layoutDirection, .rightToLeft) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment