9

I added toolbar to 2 text fields, but the toolbar shows up twice:

This is the code for my custom text field:

public struct SUIDecimalField: View {
  public var body: some View {
    TextField(placeholder, text: $value)
      .focused(focused)
      .textFieldStyle(.roundedBorder)
      .keyboardType(.decimalPad)
      .toolbar {
        ToolbarItemGroup(placement: .keyboard) {
          HStack {
            Button(LOC(.clearKeyboardButton)) {
              value = ""
            }
            Spacer()
            Button(LOC(.dismissKeyboardButton)) {
              focused.wrappedValue = false
            }
          }
        }
      }
    }
  }

As you can see i only added the toolbar to the text field. I have 2 text fields, and it showed up twice.

Looks like the toolbar is somehow shared between all text fields. I am wondering how do I resolve it? I really want to put the toolbar inside the text field implementation so that the logic is encapsulated.

enter image description here

  • This is a known thing. The only way around it is using UIKit instead – lorem ipsum Feb 26 '22 at 19:39
  • @loremipsum I am not using uikit for my project and i don't think it's possible to convert it to uikit now. –  Feb 26 '22 at 19:41
  • Look up uiviewrepresentable – lorem ipsum Feb 26 '22 at 19:50
  • Look at the orange implementation [here](https://stackoverflow.com/questions/69322939/get-live-text-when-press-button/69324686#69324686) – lorem ipsum Feb 26 '22 at 19:52
  • I meant i already have lots of infrastructure based on Swift UI's TextField, and it'd be great if we can do it with SwiftUI itself, not the UIViewRepresentable –  Feb 26 '22 at 22:20
  • I don’t think it is possible right now. This question has been asked a few times and I haven’t seen a resolution. Put in a bug report and if you can find the other questions maybe you’ll find other feedback numbers to mention. – lorem ipsum Feb 26 '22 at 23:06
  • What is the point for this 2 buttons ? For clear check [this](https://stackoverflow.com/questions/58200555/swiftui-add-clearbutton-to-textfield) and for done you can use .onSubmit – Ptit Xav Feb 27 '22 at 15:13
  • decimalPad doesn't have a done button, so you need to add your own – fer0n Nov 26 '22 at 20:28

3 Answers3

4

Try applying the .toolbar modifier on the Form itself and not on each individual TextField like so:

import SwiftUI

struct ContentView: View {
    
    @State private var value1 = ""
    @State private var value2 = ""
    @FocusState private var focused: Int?
    
    var body: some View {
        Form {
            MyTextField(value: $value1, tag: 1, focused: $focused)
                .focused($focused, equals: 1)
            MyTextField(value: $value2, tag: 2, focused: $focused)
                .focused($focused, equals: 2)
        }
        .toolbar {
            ToolbarItemGroup(placement: .keyboard) {
                HStack {
                    Button("Clear") {
                        switch focused {
                        case 1: value1 = ""
                        case 2: value2 = ""
                        default: return
                        }
                    }
                    Spacer()
                    Button("Done") {
                        focused = nil
                    }
                }
            }
        }
    }
}

struct MyTextField: View {
    
    @Binding var value: String
    var tag: Int
    var focused: FocusState<Int?>.Binding
    
    var body: some View {
        TextField("Test", text: $value)
            .textFieldStyle(.roundedBorder)
            .keyboardType(.decimalPad)
    }
}
pxlshpr
  • 986
  • 6
  • 15
  • that solved my issue with multiple TextFields in a list. I was adding the ToolBar to on particular TextField only and it worked for all TextFields but made the scroll jerky when a TextField other than the one I added the Toolbar to was active. Moving the ToolbarModifier to the List instead of the TextField solved the problem and no more jerky scroll. Thanks. – alionthego May 10 '23 at 11:05
1

this is not ideal, but it might be a start:

struct ContentView: View {
    
    @State private var value1 = ""
    @State private var value2 = ""
    @FocusState private var focused: Int?
    
    var body: some View {
        Form {
            MyTextField(value: $value1, tag: 1, focused: $focused)
                .focused($focused, equals: 1)
            
            MyTextField(value: $value2, tag: 2, focused: $focused)
                .focused($focused, equals: 2)
        }
    }
}

struct MyTextField: View {
    
    @Binding var value: String
    var tag: Int
    var focused: FocusState<Int?>.Binding
    
    var body: some View {
        TextField("Test", text: $value)
            .textFieldStyle(.roundedBorder)
            .keyboardType(.decimalPad)
        
            .toolbar {
                ToolbarItemGroup(placement: .keyboard) {
                    HStack {
                        if focused.wrappedValue == tag {
                            Button("Clear") {
                                value = ""
                            }
                            Spacer()
                            Button("Done") {
                                focused.wrappedValue = nil
                            }
                        }
                    }
                }
            }
    }
}
ChrisR
  • 9,523
  • 1
  • 8
  • 26
  • I think if you put the if statement under HStack, there will be multiple HStack with empty views inside –  Feb 26 '22 at 23:04
  • interestingly, if i put the if statement outside of HStack, the toolbar doesn't show up. I don't know why –  Feb 26 '22 at 23:16
  • The extra empty HStacks is an issue. They take up spaces too –  Feb 26 '22 at 23:23
  • So if you add 2 fields, the first field's toolbar will only occupy the left half of toolbar space, and the second field's toolbar will only occupy the right half of the toolbar space –  Feb 26 '22 at 23:23
  • you're absolutely right, this was the only way I could somehow get rid of the duplicates ... anyway this is a SwiftUI issue which hopefully will be resolved at some point. – ChrisR Feb 27 '22 at 09:54
0

I have struggled with similar situation myself, and there is no clean way. What I would suggest in your case is

  1. append a clear button (X) on the right side of the text field, this is very intuitive for the user

  2. you do not need to rewrite your code into UIKit, but can use one useful line of code (wrapped into extension of View) as described here: https://www.hackingwithswift.com/quick-start/swiftui/how-to-dismiss-the-keyboard-for-a-textfield the advantage is, that it is generic and you do not need to determine which focus state to change.

  3. add the toolbar to only one of the TextFields, then the "Dismiss" button will not be replicated in the toolbar and it will work correctly.

Not nice, not clean, but as SwiftUI as you can get and functional.

Jalian
  • 21
  • 1