1

I have some textFields that are already completed and I want to add some "Edit" buttons that will show the keyboard to the one that I press. Here is the code to one of the textFields:

Image(colorScheme == .light ? "user" : "userDark").resizable().frame(width: 30, height: 30)
TextField("Name", text: $name).textFieldStyle(PlainTextFieldStyle()).padding(.leading, 5).font(.system(size: 20))
    .autocapitalization(.words)
    .disableAutocorrection(true)
if name != localName {
    Button(action: {
            self.name = ""
    }) {
        Text("Update").font(.system(size: 15)).fontWeight(.light)
            .foregroundColor(colorScheme == .light ? Color.black : Color.white)
    }
} else if name == localName {
    Text("Edit").font(.system(size: 15)).fontWeight(.light)
        .foregroundColor(colorScheme == .light ? Color.black : Color.white)
}

This is what the textFields look like: enter image description here

Cameron Little
  • 3,487
  • 23
  • 35
Susca Bogdan
  • 991
  • 1
  • 9
  • 23

2 Answers2

5

It's very easy if you want to enable custom editMode to each different property. Here I am creating custom editMode for only name field you just create editMode for other field like contact number you provided in above image. First create an editMode property for every field like:

@State var nameInEditMode = false
@State var name = "Mr. Foo Bar"

Then your each field should look like this:

    HStack {
        Image(systemName: "person.circle").resizable().frame(width: 30, height: 30)

        if nameInEditMode {
            TextField("Name", text: $name).textFieldStyle(RoundedBorderTextFieldStyle()).padding(.leading, 5).font(.system(size: 20))
            .autocapitalization(.words)
            .disableAutocorrection(true)
        } else {
            Text(name).font(.system(size: 20))
        }

        Button(action: {
            self.nameInEditMode.toggle()
        }) {
            Text(nameInEditMode ? "Done" : "Edit").font(.system(size: 20)).fontWeight(.light)
                .foregroundColor(Color.blue)
        }
    }

For testing just copy, paste and run the above code then see it's amazing.

Md. Yamin Mollah
  • 1,609
  • 13
  • 26
  • Yes., It is really amazing. How this line `self.nameInEditMode.toggle()` calling `if-else` condition ?? If u knw., pls guide me – McDonal_11 Aug 02 '21 at 18:19
  • 1
    @McDonal_11 `@State var nameInEditMode = false` is a state variable. If any state variable changes, then its corresponding usages are automatically called. – Md. Yamin Mollah Aug 03 '21 at 05:07
2

This functionality is possible with a custom UIViewRepresentable.

This code is will create a custom TextField from a UITextField and allow you to change the parameters like font and autocapitalizationType you had.

struct CustomTextField: UIViewRepresentable {

    class Coordinator: NSObject, UITextFieldDelegate {

        @Binding var text: String
        @Binding var isEditing: Bool
        var didBecomeFirstResponder = false

        init(text: Binding<String>, editing: Binding<Bool>) {
            _text = text
            _isEditing = editing
        }

        func textFieldDidChangeSelection(_ textField: UITextField) {
            text = textField.text ?? ""
        }

        func textFieldDidBeginEditing(_ textField: UITextField) {
            isEditing = true
        }

        func textFieldDidEndEditing(_ textField: UITextField) {
            isEditing = false
        }

    }

    var placeholder = ""
    @Binding var text: String
    @Binding var isEditing: Bool
    var isFirstResponder: Bool = false
    var font = UIFont.systemFont(ofSize: 20)
    var autocapitalization = UITextAutocapitalizationType.none
    var autocorrection = UITextAutocorrectionType.default
    var borderStyle = UITextField.BorderStyle.none

    func makeUIView(context: UIViewRepresentableContext<CustomTextField>) -> UITextField {
        let textField = UITextField(frame: .zero)
        textField.delegate = context.coordinator
        textField.placeholder = placeholder
        textField.font = font
        textField.autocapitalizationType = autocapitalization
        textField.autocorrectionType = autocorrection
        textField.borderStyle = borderStyle
        return textField
    }

    func makeCoordinator() -> CustomTextField.Coordinator {
        return Coordinator(text: $text, editing: $isEditing)
    }

    func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<CustomTextField>) {
        uiView.text = text
        if isFirstResponder && !context.coordinator.didBecomeFirstResponder  {
            uiView.becomeFirstResponder()
            context.coordinator.didBecomeFirstResponder = true
        } else if !isFirstResponder && context.coordinator.didBecomeFirstResponder {
            uiView.resignFirstResponder()
            context.coordinator.didBecomeFirstResponder = false
        }
    }
}

Credit: This code is modified from an answer to a similar question by @MatteoPacini here.

I am assuming that PlainTextFieldStyle() is equivalent to UITextField.BorderStyle.none.

So in your View, to have the "Edit" Button bring up the keyboard and change to "Done" to lower the keyboard, your code would be:

Image(colorScheme == .light ? "user" : "userDark")
    .resizable()
    .frame(width: 30, height: 30)
CustomTextField(
    placeholder: "Name",
    text: $name,
    isEditing: $isEditing,
    isFirstResponder: isEditing,
    font: .systemFont(ofSize: 20),
    autocapitalization: .words,
    autocorrection: .no,
    borderStyle: .none
)
    .padding(.leading, 5)
if name != localName {
    Button(action: {
        self.name = ""
    }) {
        Text("Update")
            .font(.system(size: 15))
            .fontWeight(.light)
            .foregroundColor(colorScheme == .light ? Color.black : Color.white)
    }
} else if name == localName {
    Button(action: {
        self.isEditing.toggle()
    }) {
        Text(self.isEditing ? "Cancel" : "Edit")
            .font(.system(size: 15))
            .fontWeight(.light)
            .foregroundColor(colorScheme == .light ? Color.black : Color.white)
    }
}

With a Bool indicating if the TextField is editing:

@State var isEditing = false

If the code you provided defines the cell of a List or Form, you might replace this with an IndexSet or Optional<IndexPath> of the editing cell.

Also, if you do not want the "Done" Button, simply change the Button to:

Button(action: {
    self.isEditing = true
}) {
    Text("Edit")
        .font(.system(size: 15))
        .fontWeight(.light)
        .foregroundColor(colorScheme == .light ? Color.black : Color.white)
}

Tested on a Swift Playground in Xcode-beta-2

https://stackoverflow.com/a/56508132/13231930