Floating Text Field In SwiftUI

Mobile Apps Academy
4 min readMar 16, 2024

In this article, We will explore how to implement a Floating Text Field Using SwiftUI

Before proceeding, please consider subscribing to our YOUTUBE CHANNEL

It gives us a lot of motivation to produce high-quality content for you guys.

if you are interested in watching the video tutorial, Check out below.

Let's start by creating the script FloatingTextField

struct FloatingTextField: View {
var body: some View {
ZStack(alignment: .leading){

}
}
}

We need a few variables to start with which include placeholderText and few others

struct FloatingTextField: View {

let placeholderText: String
@State private var text: String = ""

let animation: Animation = .spring(response: 0.1, dampingFraction: 0.6)

@State private var placeholderOffset: CGFloat
@State private var scaleEffectValue: CGFloat

private var onTextAction: ((_ oldValue: String, _ newValue: String) -> ())?

init(placeholderText: String, placeholderOffset: CGFloat = 0, scaleEffectValue: CGFloat = 1, onTextAction: ((_: String, _: String) -> Void)? = nil) {
self.placeholderText = placeholderText
self.placeholderOffset = placeholderOffset
self.scaleEffectValue = scaleEffectValue
self.onTextAction = onTextAction
}

var body: some View {
ZStack(alignment: .leading){

}
}
}

We’ll be using a Text view and TextField which is added to the ZStack. Also under the overlay a RoundedRectangle is added.

struct FloatingTextField: View {

let placeholderText: String
@State private var text: String = ""

let animation: Animation = .spring(response: 0.1, dampingFraction: 0.6)

@State private var placeholderOffset: CGFloat
@State private var scaleEffectValue: CGFloat

private var onTextAction: ((_ oldValue: String, _ newValue: String) -> ())?

init(placeholderText: String, placeholderOffset: CGFloat = 0, scaleEffectValue: CGFloat = 1, onTextAction: ((_: String, _: String) -> Void)? = nil) {
self.placeholderText = placeholderText
self.placeholderOffset = placeholderOffset
self.scaleEffectValue = scaleEffectValue
self.onTextAction = onTextAction
}

var body: some View {
ZStack(alignment: .leading){
Text(placeholderText)
.foregroundStyle($text.wrappedValue.isEmpty ? Color.white : .gray)
.font($text.wrappedValue.isEmpty ? .headline : .caption)
.offset(y: placeholderOffset)
.scaleEffect(scaleEffectValue, anchor: .leading)

TextField("", text: $text)
.font(.headline)
.foregroundStyle(Color.yellow)
}
.padding()
.padding(.vertical, 5)
.overlay {
RoundedRectangle(cornerRadius: 10)
.stroke(.gray, lineWidth: 2)
}
}
}

Let's use this FloatingTextField inside our ContentView

struct ContentView: View {
var body: some View {
ZStack {
Color.black
.ignoresSafeArea()
ZStack {
FloatingTextField(placeholderText: "Name")
}
.padding()
}
}
}

As you can see, there is no way to tell if text is changed or not.

To fix this, we added a onTextChange modifier.

extension FloatingTextField {
public func onTextChange(_ onTextAction: @escaping ((_ oldValue: String, _ newValue: String) -> ())) -> Self {

var view = self
view.onTextAction = onTextAction
return view
}
}

This modifier is called onChange function.

struct FloatingTextField: View {

let placeholderText: String
@State private var text: String = ""

let animation: Animation = .spring(response: 0.1, dampingFraction: 0.6)

@State private var placeholderOffset: CGFloat
@State private var scaleEffectValue: CGFloat

private var onTextAction: ((_ oldValue: String, _ newValue: String) -> ())?

init(placeholderText: String, placeholderOffset: CGFloat = 0, scaleEffectValue: CGFloat = 1, onTextAction: ((_: String, _: String) -> Void)? = nil) {
self.placeholderText = placeholderText
self.placeholderOffset = placeholderOffset
self.scaleEffectValue = scaleEffectValue
self.onTextAction = onTextAction
}

var body: some View {
ZStack(alignment: .leading){
Text(placeholderText)
.foregroundStyle($text.wrappedValue.isEmpty ? Color.white : .gray)
.font($text.wrappedValue.isEmpty ? .headline : .caption)
.offset(y: placeholderOffset)
.scaleEffect(scaleEffectValue, anchor: .leading)

TextField("", text: $text)
.font(.headline)
.foregroundStyle(Color.yellow)
}
.padding()
.padding(.vertical, 5)
.overlay {
RoundedRectangle(cornerRadius: 10)
.stroke(.gray, lineWidth: 2)
}
.onChange(of: text) { oldValue, newValue in
withAnimation(animation) {
placeholderOffset = $text.wrappedValue.isEmpty ? 0 : -25
scaleEffectValue = $text.wrappedValue.isEmpty ? 1 : 0.75
}

onTextAction?(oldValue, newValue)
}
}
}

after adding this modifier, our code looks like below.

struct ContentView: View {
var body: some View {
ZStack {
Color.black
.ignoresSafeArea()
ZStack {
FloatingTextField(placeholderText: "Name")
.onTextChange { oldValue, newValue in
print(newValue)
}
}
.padding()
}
}
}

That’s it. Below is what we get.

Once again Thanks for stopping by.
Do check out our YOUTUBE CHANNEL

Social Handles

Instagram : https://www.instagram.com/mobileappsacademy/

Twitter : https://twitter.com/MobileAppsAcdmy

LinkedIn : https://www.linkedin.com/company/mobile-apps-academy/

--

--

Mobile Apps Academy

Welcome to Mobile Apps Academy, your go-to channel for all things mobile app development!