SwiftUI - отклонение клавиатуры при нажатии в любом месте представления - проблемы с другими интерактивными элементами - PullRequest
2 голосов
/ 22 февраля 2020

У меня есть TextField и некоторые активные элементы, такие как Button, Picker внутри вида. Я хочу отклонить клавиатуру, когда использование выходит за пределы TextField. Используя ответы на этот вопрос , я достиг этого. Однако проблема связана с другими активными элементами.

Когда я нажимаю Button, действие происходит, но клавиатура не закрывается. То же самое с Toggle переключателем. Когда я нажимаю на одну секцию SegmentedStyle Picker, клавиатура отключается, но выбор выбора не меняется.

Вот мой код.


struct SampleView: View {

    @State var selected = 0
    @State var textFieldValue = ""

    var body: some View {
        VStack(spacing: 16) {
            TextField("Enter your name", text: $textFieldValue)
                .padding(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
                .background(Color(UIColor.secondarySystemFill))
                .cornerRadius(4)


            Picker(selection: $selected, label: Text(""), content: {
                Text("Word").tag(0)
                Text("Phrase").tag(1)
                Text("Sentence").tag(2)
            }).pickerStyle(SegmentedPickerStyle())            

            Button(action: {
                self.textFieldValue = "button tapped"
            }, label: {
                Text("Tap to change text")
            })

        }.padding()
        .onTapGesture(perform: UIApplication.dismissKeyboard)
//        .gesture(TapGesture().onEnded { _ in UIApplication.dismissKeyboard()})
    }
}

public extension UIApplication {

    static func dismissKeyboard() {
        let keyWindow = shared.connectedScenes
                .filter({$0.activationState == .foregroundActive})
                .map({$0 as? UIWindowScene})
                .compactMap({$0})
                .first?.windows
                .filter({$0.isKeyWindow}).first
        keyWindow?.endEditing(true)
    }
}

Как вы можете видеть в коде, я попробовал оба варианта, чтобы получить жест касания, и ничего не получилось.

Ответы [ 3 ]

2 голосов
/ 22 февраля 2020

Вы можете создать расширение в View следующим образом:

extension View {
  func endTextEditing() {
    UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder),
                                    to: nil, from: nil, for: nil)
  }
}

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

.onTapGesture {

      self.endTextEditing()
} 

Я только что видел это решение в недавнее руководство по raywenderlich, так что я полагаю, что в настоящее время это лучшее решение.

1 голос
/ 22 февраля 2020

Отклонение клавиатуры при нажатии в любом месте (как предлагали другие) может привести к очень трудным для поиска ошибкам (или нежелательному поведению).

  1. вы теряете встроенное поведение TextField по умолчанию, например частичное выделение текста, копирование, совместное использование и т. д. c.
  2. onCommit не называется

Я предлагаю вам подумать о маскировании жестов на основе состояния редактирования ваших полей

/// Attaches `gesture` to `self` such that it has lower precedence
    /// than gestures defined by `self`.
    public func gesture<T>(_ gesture: T, including mask: GestureMask = .all) -> some View where T : Gesture

это поможет нам написать

.gesture(TapGesture().onEnded({
            UIApplication.shared.windows.first{$0.isKeyWindow }?.endEditing(true)
        }), including: (editingFlag) ? .all : .none)

Нажмите на измененный вид, чтобы закрыть клавиатуру, но только если editingFlag == true. Не применять его на TextField! В противном случае мы снова в начале истории: -)

Этот модификатор поможет нам решить проблему с Picker, но не с Button. Это легко решить, пока клавиатура не отключается от собственного обработчика действий. У нас нет никаких других элементов управления, поэтому мы почти закончили

Наконец, мы должны найти решение для остальной части View, поэтому нажатие в любом месте (исключая наши текстовые поля) отклоняет клавиатуру . Использование ZStack, заполненного прозрачным представлением, вероятно, самое простое решение.

Давайте посмотрим на все это в действии (копировать - вставить - запустить в симуляторе Xcode)

import SwiftUI
struct ContentView: View {

    @State var selected = 0

    @State var textFieldValue0 = ""
    @State var textFieldValue1 = ""

    @State var editingFlag = false

    @State var message = ""

    var body: some View {
        ZStack {
            // TODO: make it Color.clear istead yellow
            Color.yellow.opacity(0.1).onTapGesture {
                UIApplication.shared.windows.first{$0.isKeyWindow }?.endEditing(true)
            }
            VStack {

                TextField("Salutation", text: $textFieldValue0, onEditingChanged: { editing in
                    self.editingFlag = editing
                }, onCommit: {
                    self.onCommit(txt: "salutation commit")
                })
                    .padding()
                    .background(Color(UIColor.secondarySystemFill))
                    .cornerRadius(4)

                TextField("Welcome message", text: $textFieldValue1, onEditingChanged: { editing in
                    self.editingFlag = editing
                }, onCommit: {
                    self.onCommit(txt: "message commit")
                })
                    .padding()
                    .background(Color(UIColor.secondarySystemFill))
                    .cornerRadius(4)

                Picker(selection: $selected, label: Text(""), content: {
                    Text("Word").tag(0)
                    Text("Phrase").tag(1)
                    Text("Sentence").tag(2)
                })
                    .pickerStyle(SegmentedPickerStyle())
                    .gesture(TapGesture().onEnded({
                        UIApplication.shared.windows.first{$0.isKeyWindow }?.endEditing(true)
                    }), including: (editingFlag) ? .all : .none)


                Button(action: {
                    self.textFieldValue0 = "Hi"
                    print("button pressed")
                    UIApplication.shared.windows.first{$0.isKeyWindow }?.endEditing(true)
                }, label: {
                    Text("Tap to change salutation")
                        .padding()
                        .background(Color.yellow)
                        .cornerRadius(10)
                })

                Text(textFieldValue0)
                Text(textFieldValue1)
                Text(message).font(.largeTitle).foregroundColor(Color.red)

            }

        }
    }

    func onCommit(txt: String) {
        print(txt)
        self.message = [self.textFieldValue0, self.textFieldValue1].joined(separator: ", ").appending("!")
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

enter image description here

Если вы пропустите onCommit (он не вызывается при нажатии вне TextField), просто добавьте его в TextField onEditingChanged (он имитирует ввод на клавиатуре)

TextField("Salutation", text: $textFieldValue0, onEditingChanged: { editing in
    self.editingFlag = editing
    if !editing {
        self.onCommit(txt: "salutation")
    }
 }, onCommit: {
     self.onCommit(txt: "salutation commit")
 })
     .padding()
     .background(Color(UIColor.secondarySystemFill))
     .cornerRadius(4)
0 голосов
/ 22 февраля 2020

Вы можете установить .allowsHitTesting(false) на свой Picker, чтобы игнорировать нажатие на VStack

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...