SwiftUI - 在视图中的任意位置点击时关闭键盘 - 其他交互元素的问题

Imt*_*ath 4 ios swift swiftui

我有一个TextField和一些可操作的元素,例如ButtonPicker在视图中。当使用在TextField. 使用this question中的答案,我实现了它。然而,问题来自于其他可操作的项目。

当我点击 a 时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)
    }
}

Run Code Online (Sandbox Code Playgroud)

正如您在代码中看到的那样,我尝试了这两个选项来获取点击手势,但没有任何效果。

use*_*734 8

通过点击任意位置关闭键盘(就像其他人建议的那样)可能会导致很难发现错误(或不需要的行为)。

  1. 您失去了默认的内置 TextField 行为,例如部分文本选择、复制、共享等。
  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
Run Code Online (Sandbox Code Playgroud)

这有助于我们写

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

点击修改后的视图将关闭键盘,但前提是editingFlag == true不要将其应用到 TextField 上!否则我们又回到了故事的开始:-)

这个修饰符将帮助我们解决使用Picker而不是使用的问题Button。当将键盘从其自己的操作处理程序中关闭时,这个问题很容易解决。我们没有任何其他控件,所以我们几乎完成了

最后,我们必须找到视图其余部分的解决方案,因此点击任意位置(不包括我们的文本字段)关闭键盘。使用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()
    }
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

如果您错过了 onCommit(在点击 TextField 外部时不会调用它),只需将其添加到您的 TextField 中onEditingChanged(它模仿在键盘上键入 Return)

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)
Run Code Online (Sandbox Code Playgroud)


Mul*_*uli 6

我想进一步采用 Mark Ts 的答案,并将整个函数添加到以下扩展中View

extension View {
    func hideKeyboardWhenTappedAround() -> some View  {
        return self.onTapGesture {
            UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), 
                  to: nil, from: nil, for: nil)
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

然后可以这样调用:

var body: some View {
    MyView()
      // ...
      .hideKeyboardWhenTappedAround()
      // ...
}
Run Code Online (Sandbox Code Playgroud)

  • 对我来说工作完美! (2认同)

Mar*_* T. 5

您可以像这样在 View 上创建扩展

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

并将其用于要关闭键盘的视图。

.onTapGesture {

      self.endTextEditing()
} 
Run Code Online (Sandbox Code Playgroud)

我刚刚在最近的 raywenderlich 教程中看到了这个解决方案,所以我认为它是目前最好的解决方案。

  • 如果 Picker 和 Button 应该可以工作,您将在哪个控件上应用 .onTapGesture 修饰符?不幸的是,这种方法在这里不可用...... (4认同)

Mac*_*c3n 0

您可以.allowsHitTesting(false)将 Picker 设置为忽略 VStack 上的点击