Categories
iOS Development

Create a labeled TextField in SwiftUI

Today, I wanted to create a labeled TextField in SwiftUI. More specifically the label should appear as a small text above the TextField. My first quick hack was to just add a label above a TextField. Obviously, we can do better. Let me show you how.

LabeledContent

As a major thing in SwiftUI is its declarative syntax, it is not a big surprise that there is already an official element which does exactly what we want, i.e. labeling content. And it is also named like that.

According to the official docs, it is “A container for attaching a label to a value-bearing view.” (see https://developer.apple.com/documentation/swiftui/labeledcontent). It is really exactly what we want. I mean almost.

Default behavior

The following snippet shows the bare minimum (with some styling of the textfield):

LabeledContent {
 TextField("", text: $text)
  .textFieldStyle(.roundedBorder)
  .frame(width: 200)
} label: {
 Text("Name")
}
Code language: Swift (swift)

It is a labeled Textfield but it is not exactly looking like what we wanted. It will result in something like this:

LabeledContent wrapped around a TextField in SwiftUI

As we see, the default layout is horizontal and not our desired vertical layout. Also there is a huge margin we do not want. Can we modify this? Yes, of course.

Custom LabeledContentStyle

A lot of SwiftUI elements can be modified by using an associated style configuration. Luckily, this is also possible for LabeledContent. By doing this instead of building a custom View, we still get all the nice SwiftUI sugar and accessibility features for free.

There is the following modifier we can use for that:

func labeledContentStyle<S>(_ style: S) -> some View where S : LabeledContentStyleCode language: HTML, XML (xml)

As we see, this modifier needs a style that conforms to LabeledContentStyle passed into it. Based on the passed style, the LabeledContent is styled however we want.

  1. We want to have a label above the textfield (we need a VStack)
  2. The label should be a little smaller than the TextField‘s input (we modify the label with some smaller font)
  3. Label and textfield should be aligned to the leading edge (set alignment of the VStack to .leading).

With these requirements, we can build a really simple struct conforming to LabeledContentStyle:

struct TopLabeledStyleConfig: LabeledContentStyle {

 func makeBody(configuration: Configuration) -> some View {
  VStack(alignment: .leading) {
   configuration.label
    .font(.caption)
   configuration.content
  }
 }
}
Code language: Swift (swift)

After that, we can append our newly created style via the labeledContentStyle modifier:

LabeledContent {
 TextField("", text: $text)
  .textFieldStyle(.roundedBorder)
  .frame(width: 200)
} label: {
 Text("Name")
}
.labeledContentStyle(TopLabeledStyleConfig())
Code language: Swift (swift)

Which will result in this:

Textfield wrapped with LabeledContent using custom style

Much better right?

Of course now you can fine tune it, change the margin, font type, etc. however you like. You could also think about adding small animations when focussing the textfield and so on.

TopLabeledTextField

If you want to reuse this labeled and styled TextField in your SwiftUI views more often, it makes sense to create a reusable component. By that you do not have to think about adding your modifier manually.

Just put everything in one file and you have your own TopLabeledTextField:

struct TopLabeledStyleConfig: LabeledContentStyle {

 func makeBody(configuration: Configuration) -> some View {
  VStack(alignment: .leading) {
   configuration.label
    .font(.caption)
   configuration.content
  }
 }

}

struct TopLabeledTextField: View {

 @Binding var text: String
 var placeholderText: String

    var body: some View {
  LabeledContent {
   TextField("", text: $text)
    .textFieldStyle(.roundedBorder)
    .frame(width: 200)
  } label: {
   Text(placeholderText)
  }
  .labeledContentStyle(TopLabeledStyleConfig())
    }
}
Code language: Swift (swift)

Conclusion

I hope you learned something new today. If there is one thing you should take away: Always look around for style configurations on any SwiftUI elements. This is often a better way to style things in contrast to building together more or less generic Views.

E.g. when you want to add custom styled Toggle elements, this is also the way to go. No need for basic View and onTapGesture.

Leave a Reply

Your email address will not be published. Required fields are marked *