SwiftUI - добавление кнопки панели инструментов клавиатуры только для одного текстового поля добавляет ее для всех текстовых полей.

Задний план

У меня есть два s, один из которых имеет тип клавиатуры .

Учитывая, что при использовании клавиатуры с десятичной панелью, чтобы закрыть ее, нет кнопки «Готово», скорее, как клавиша возврата стандартной клавиатуры, я хотел бы добавить кнопку «Готово» на панели инструментов над клавиатурой только для десятичной клавиатуры. в SwiftUI.

Проблема

Добавление a к любому по какой-либо причине добавляет его вместо этого ко всем s! Я пробовал условные модификаторы, используя сфокусированные состояния и проверяя наличие value (но по какой-то причине он не устанавливается при проверке, может быть, для заказа?), и он все равно добавляет панель инструментов над клавиатурой для обоих s.

Как я могу иметь только для моего сингла, который принимает цифры, а не для другого, который принимает строку?

Код

Обратите внимание, что я попытался создать минимальный пример, который вы можете просто скопировать и вставить в Xcode и запустить его самостоятельно. В Xcode 13.2 есть некоторые проблемы с отображением клавиатуры для s для меня, особенно внутри листа, поэтому, возможно, требуется симулятор, чтобы запустить его правильно и вызвать клавиатуру с помощью cmd+K.

      import SwiftUI

struct TestKeyboard: View {
    @State var str: String = ""
    @State var num: Float = 1.2

    @FocusState private var focusedField: Field?
    private enum Field: Int, CaseIterable {
        case amount
        case str
    }

    var body: some View {
        VStack {
            Spacer()
            
            // I'm not adding .toolbar here...
            TextField("A text field here", text: $str)
                .focused($focusedField, equals: .str)

            // I'm only adding .toolbar here, but it still shows for the one above..
            TextField("", value: $num, formatter: FloatNumberFormatter())
                .keyboardType(.decimalPad)
                .focused($focusedField, equals: .amount)
                .toolbar {
                    ToolbarItem(placement: .keyboard) {
                        Button("Done") {
                            focusedField = nil
                        }
                    }
                }

            Spacer()
        }
    }
}

// So you can preview it quickly
struct TestKeyboard_Previews: PreviewProvider {
    static var previews: some View {
        TestKeyboard()
    }
}

7 ответов

Попробуйте сделать содержимое панели инструментов условным и переместите панель инструментов наружу, как показано ниже. (Сейчас нет возможности проверить - просто идея)

Примечание: проверьте на реальном устройстве

      var body: some View {
    VStack {
        Spacer()
        
        TextField("A text field here", text: $str)
            .focused($focusedField, equals: .str)

        TextField("", value: $num, formatter: FloatNumberFormatter())
            .focused($focusedField, equals: .amount)
            .keyboardType(.decimalPad)

        Spacer()
    }
    .toolbar {          // << here !!
        ToolbarItem(placement: .keyboard) {
            if field == .amount {             // << here !!
               Button("Done") {
                  focusedField = nil
               }
            }
        }
    }

}

Я много пробовал, но остановился на том, что ниже.

          .focused($focusedField, equals: .zip)
                .toolbar{
                    ToolbarItem(placement: .keyboard) {
                        switch focusedField{
                        case .zip:
                            HStack{
                                Spacer()
                                Button("Done"){
                                    focusedField = nil
                                }
                            }
                        default:
                                Text("")
                        }
                    }
                }

Это мое решение:

      func textFieldSection(title: String,
                      text: Binding<String>,
                      keyboardType: UIKeyboardType,
                      focused: FocusState<Bool>.Binding,
                      required: Bool) -> some View {
    TextField(
        vm.placeholderText(isRequired: required),
        text: text
    )
    .focused(focused)
    .toolbar {
        ToolbarItemGroup(placement: .keyboard) {
            if focused.wrappedValue {
                Spacer()
                Button {
                    focused.wrappedValue = false
                } label: {
                    Text("Done")
                }
            }
        }
    }
}

Для моего проекта у меня есть пятьTextFieldпредставлений в одном представлении, поэтому я создал этот метод в расширении представления.

Я передаю уникальныйFocusState<Bool>.Bindingзначение и используйте его в закрытии ToolbarItemGroup, чтобы определить, следует ли отображать содержимое (пробел, кнопка). Если конкретное TextFieldfocused, мы отображаем содержимое панели инструментов (все остальные текстовые поля без фокуса не будут отображаться).

Решение для возврата цифровой клавиатуры в SwiftUI, кнопка панели инструментов над клавиатурой, сфокусированное поле

      struct NumberOfBagsView:View{
   @FocusState var isInputActive: Bool
   @State var phoneNumber:String = ""
   TextField("Place holder",
                  text: $phoneNumber,
                  onEditingChanged: { _ in
                
            //do actions while writing something in text field like text limit
            
        })
        .keyboardType(.numberPad)
        .focused($isInputActive)
        .toolbar {
            ToolbarItem(placement: .keyboard) {

                Button("Done") {
                    print("done clicked")
                    isInputActive = false
                }
                
            }
        }
}

Используя самоанализ , вы можете сделать что-то подобное в любой части вашего представления:

      .introspectTextField { textField in
                    textField.inputAccessoryView = UIView.getKeyboardToolbar {
                        textField.resignFirstResponder()
                    }
            }

и для getKeyboardToolbar:

      extension UIView {

static func getKeyboardToolbar( _ callback: @escaping (()->()) ) -> UIToolbar {
        let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 44))
        let doneButton = CustomBarButtonItem(title: "Done".localized, style: .done) { _ in
            callback()
        }
        
        let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
        toolBar.items = [space, doneButton]
        
        return toolBar
    }

}

и для CustomBarButtonItemэто элемент кнопки панели, который принимает закрытие

      import UIKit

class CustomBarButtonItem: UIBarButtonItem {
    typealias ActionHandler = (UIBarButtonItem) -> Void

    private var actionHandler: ActionHandler?

    convenience init(image: UIImage?, style: UIBarButtonItem.Style, actionHandler: ActionHandler?) {
        self.init(image: image, style: style, target: nil, action: #selector(barButtonItemPressed(sender:)))
        target = self
        self.actionHandler = actionHandler
    }

    convenience init(title: String?, style: UIBarButtonItem.Style, actionHandler: ActionHandler?) {
        self.init(title: title, style: style, target: nil, action: #selector(barButtonItemPressed(sender:)))
        target = self
        self.actionHandler = actionHandler
    }

    convenience init(barButtonSystemItem systemItem: UIBarButtonItem.SystemItem, actionHandler: ActionHandler?) {
        self.init(barButtonSystemItem: systemItem, target: nil, action: #selector(barButtonItemPressed(sender:)))
        target = self
        self.actionHandler = actionHandler
    }

    @objc func barButtonItemPressed(sender: UIBarButtonItem) {
        actionHandler?(sender)
    }
}

Я нашел упаковку каждого TextFieldв своем собственном NavigationViewдает каждому свой собственный контекст и, таким образом, уникальную панель инструментов. Это кажется неправильным, и я видел предупреждения об ограничениях в консоли. Используйте что-то вроде этого:

         var body: some View {
    VStack {
        Spacer()
        
        // I'm not adding .toolbar here...
        NavigationView {
          TextField("A text field here", text: $str)
            .focused($focusedField, equals: .str)
        }
        // I'm only adding .toolbar here, but it still shows for the one above..
        NavigationView {
          TextField("", value: $num, formatter: FloatNumberFormatter())
            .keyboardType(.decimalPad)
            .focused($focusedField, equals: .amount)
            .toolbar {
                ToolbarItem(placement: .keyboard) {
                    Button("Done") {
                        focusedField = nil
                    }
                }
            }
        }
        Spacer()
    }
}

Есть работа. Но другое TextField по-прежнему будет отображать панель инструментов.

      struct TextFieldWithToolBar<Label, Toolbar>: View where Label: View, Toolbar: View {
    @Binding public var text: String
    public let toolbar: Toolbar?

    @FocusState private var focus: Bool

    var body: some View {
        TextField(text: $text, label: { label })
            .focused($focus)
            .toolbar {
                ToolbarItemGroup(placement: .keyboard) {
                    ZStack(alignment: .leading) {
                        if focus {
                            HStack {
                                toolbar
                                Spacer()
                                Button("Done") {
                                    focus = false
                                }
                            }
                            .frame(width: UIScreen.main.bounds.size.width - 12)
                        }
                    }
                }
            }
    }
}

TextFieldWithToolBar("Name", text: $name)
TextFieldWithToolBar("Name", text: $name){
    Text("Only Brand")
}
TextField("Name", "Set The Name", text: $name)

с Готово с панелью инструментов без

Другие вопросы по тегам