WWDC21 SALE: Save 50% on all my Swift books and bundles! >>

Code Cleanup - Customizing a TextField

Forums > SwiftUI

What I have is sort of working, but I don't think it is the best way of doing it. I want to create a custom TextField, where I can do 2 things (and I need to do both things).

  1. I want to give a TextField a custom property (in this case isHighlighted which is a bool).
  2. I also want to be able to set a default look. If I have a bunch of TextFields in an app, I'd like them all to have the same look. But I would also like the ability to override that look. In the code below in the ContentView, I try to change the background of the TextField to green from the default blue, but of course I'm not doing this properly, so it creates a green background around the TextField, instead of editing the TextField itself.

So how I accomplish both of these items (extension of TextField and a View Modifier?)

struct TextFieldView: View {

    // Constants, so all "TextFields will be the same in the app"
    let fontsize: CGFloat = 14
    let backgroundColor = Color.blue
    let textColor = Color.white

    // The @State Object
    @Binding var field: String

    // A custom variable for a "TextField"
    @State var isHighlighted = false

    var body: some View {
        TextField(field, text: $field)
            .font(Font.system(size: fontsize))
            .padding()
            .background(RoundedRectangle(cornerRadius: 10).fill(backgroundColor))
            .foregroundColor(textColor)
            .padding()
    }
}

struct ContentView: View {

    @State private var name = "@TwoStraws"
    var body: some View {
        VStack {
            Text(name)
            TextFieldView(field: $name)
                .background(Color.green)
        }
    }
}

   

The way I might do this is.

struct TextFieldView: View {

    // Constants, so all "TextFields will be the same in the app"
    let fontSize: CGFloat
    let backgroundColor: Color
    let textColor: Color

    // The @State Object
    @Binding var field: String

    // A custom variable for a "TextField"
    @Binding var isHighlighted: Bool

    init(field: Binding<String>, isHighlighted: Binding<Bool>, fontSize: CGFloat = 14, backgroundColor: Color = .blue, textColor:Color = .white) {
        self._field = field
        self._isHighlighted = isHighlighted
        self.fontSize = fontSize
        self.backgroundColor = backgroundColor
        self.textColor = textColor
    }

    var body: some View {
        TextField(field, text: $field)
            .font(Font.system(size: fontSize))
            .padding()
            .background(RoundedRectangle(cornerRadius: 10).fill(backgroundColor))
            .foregroundColor(textColor)
            .padding()
    }
}

then when you call the TextFieldView you can add fontSize, backgroundColor, or textColor or leave them as they have defaults. was not sure what the isHighlighted is for as it not in the code.

struct ContentView: View {
    @State private var name = "@TwoStraws"
    @State private var isHighlight = false

    var body: some View {
        VStack {
            Text(name)
            TextFieldView(field: $name, isHighlighted: $isHighlight, backgroundColor: .green)
        }
    }
}

Or just add a modiflier to the TextField

struct TextFieldModifier: ViewModifier {
    let fontSize: CGFloat
    let backgroundColor: Color
    let textColor: Color

    func body(content: Content) -> some View {
        content
            .font(Font.system(size: fontSize))
            .padding()
            .background(RoundedRectangle(cornerRadius: 10).fill(backgroundColor))
            .foregroundColor(textColor)
            .padding()
    }
}

extension View {
    func textFieldModifier(fontSize: CGFloat = 14, backgroundColor: Color = .blue, textColor: Color = .white) -> some View {
        self.modifier(TextFieldModifier(fontSize: fontSize, backgroundColor: backgroundColor, textColor: textColor))
    }
}

then you can just use the standard TextField

struct ContentView: View {
    @State private var name = "@TwoStraws"

    var body: some View {
        VStack {
            Text(name)
            TextField("Enter Name", text: $name)
                .textFieldModifier(backgroundColor: .green)

        }
    }
}

   

The init approach makes sense. I might be missing something, and I want to make sure there isn't a way for this to be done.

What's the purpose of all of this? I have created accessory buttons for they keyboard. There are 3 - Cancel / Clear / Done. They pop up when the keyboard does, and move the TextFieldView above the keyboard and buttons if they would be underneath it. But as I'm using this custom TextFieldView (also has TextEditorView), it's sort of limited if anyone else ever wanted to use it. If they could use modifiers to the TextFieldView and it would translate to the TextField inside that, it would be much more versatile.

I'll make another post with all the code for the buttons.

   

You can play around with making a custom TextFieldStyle, like so:

import SwiftUI

struct CustomTextFieldStyle: TextFieldStyle {
    func _body(configuration: TextField<_Label>) -> some View {
        configuration
            .font(.custom("American Typewriter", size: 24))
            .background(Color.red)
            .border(Color.purple)
            .cornerRadius(8)
    }
}

struct CustomTextField: View {
    @State private var text: String = ""

    var body: some View {
        TextField("Test", text: $text)
            .textFieldStyle(CustomTextFieldStyle())
            .padding()
            .background(Color.green)
    }
}

struct CustomTextField_Previews: PreviewProvider {
    static var previews: some View {
        CustomTextField()
    }
}

Which ends up like:

custom textfieldstyle

   

Save money with our WWDC sale!

SAVE 50% To celebrate WWDC21, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.