【问题标题】:TextField SwiftUI Dismiss KeyboardTextField SwiftUI 关闭键盘
【发布时间】:2020-05-17 16:07:57
【问题描述】:

如何在用户使用 SwiftUI 在TextField 之外单击后关闭键盘?

我使用 SwiftUI 创建了一个TextField,但如果用户在TextField 之外单击,我找不到任何关闭键盘的解决方案。我查看了TextFieldSwiftUI TextField documentation 的所有属性,但找不到与关闭键盘相关的任何内容。

这是我的视图代码:

struct InputView: View {
    @State var inputValue : String = ""
    var body: some View {

        VStack(spacing: 10) {
            TextField("$", text: $inputValue)
                .keyboardType(.decimalPad)
        }
    }
}

【问题讨论】:

    标签: ios swift keyboard swiftui textfield


    【解决方案1】:

    这可以通过视图修饰符来完成。

    代码

    public extension View {
        func dismissKeyboardOnTap() -> some View {
            modifier(DismissKeyboardOnTap())
        }
    }
    
    public struct DismissKeyboardOnTap: ViewModifier {
        public func body(content: Content) -> some View {
            #if os(macOS)
            return content
            #else
            return content.gesture(tapGesture)
            #endif
        }
    
        private var tapGesture: some Gesture {
            TapGesture().onEnded(endEditing)
        }
    
        private func endEditing() {
            UIApplication.shared.connectedScenes
                .filter {$0.activationState == .foregroundActive}
                .map {$0 as? UIWindowScene}
                .compactMap({$0})
                .first?.windows
                .filter {$0.isKeyWindow}
                .first?.endEditing(true)
        }
    }
    

    用法

    backgroundView()
       .dismissKeyboardOnTap()
    

    在此处查看演示:https://github.com/youjinp/SwiftUIKit

    【讨论】:

    • 这有一个不受欢迎的副作用,如果您将它完全放在 NavigationView 上,则来自 NavigationView 的后退按钮将停止工作,但可以通过将其添加到 NavigationView 的子级来修复
    • *以及按钮和导航链接(以及其他)
    • 不起作用。第一次关闭键盘后,点击 textview 时我无法取回键盘。它只是自动关闭。我检查了 UIApplication endEediting 在第一次之后没有被调用,所以不是这样。
    【解决方案2】:

    这是使用 DragGesture 的解决方案。

    struct ContentView: View {
        @State var text: String = ""
        var body: some View {
            VStack {
                TextField("My Text", text: $text)
                    .keyboardType(.decimalPad)
            }
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
            .edgesIgnoringSafeArea(.all)
            .gesture(
                TapGesture()
                    .onEnded { _ in
                        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
                }
            )
        }
    }
    

    【讨论】:

    • 这是一个解决方案。不幸的是,它只有在用户点击VStack 时才有效。如果用户单击任何不是堆栈的区域,它将不起作用。我在question 中找到了解决它的答案。如果我们创建一个特定类型的背景视图并将我们视图的所有元素嵌入其中,它将适用于用户单击视图上的任何位置。
    • 上述解决方案都有一个重要的警告:它们会禁用具有所需操作的点击,例如,在带有NavigationLink 的列表项上。可以在这里找到一种可行的解决方案:forums.developer.apple.com/thread/127196
    【解决方案3】:
    TextField("Phone Number", text: $no)
        .keyboardType(.numbersAndPunctuation)
        .padding()
        .background(Color("4"))
        .clipShape(RoundedRectangle(cornerRadius: 10))
        .offset(y:-self.value).animation(.spring()).onAppear() {
             NotificationCenter.default.addObserver(forName:UIResponder.keyboardWillShowNotification, object: nil, queue: .main){ (notif)in
            let value  = notif.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! CGRect
                let height = value.height
                self.value = height
    
    
            }
            NotificationCenter.default.addObserver(forName:UIResponder.keyboardWillHideNotification, object: nil, queue: .main){ (notification)in
            self.value = 0
        }
    }
    
    

    【讨论】:

      【解决方案4】:

      在 SwiftUI 3 中,@FocusState 包装器可用于从可编辑字段中移除或切换焦点。当焦点从字段中移除时,键盘关闭。因此,在您的情况下,只需为TextView 的周围空间提供空间和手势即可。

      struct ContentView: View {
          @State var inputValue : String = ""
          @FocusState private var inputIsFocused: Bool
          var body: some View {
              VStack(spacing: 10) {
                  TextField("$", text: $inputValue)
                      .keyboardType(.decimalPad)
                      .border(Color.green)
                      .focused($inputIsFocused)
                  
              }
              .frame(maxHeight: .infinity) // If input is supposed to be in the center
              .background(.yellow)
              .onTapGesture {
                  inputIsFocused = false
              }
          }
      }  
      

      但我们可以用@FocusState 做更多有趣的事情。如何在表单中从一个字段切换到另一个字段。如果您轻按,键盘也会关闭。

      struct ContentView: View {
          enum Field {
              case firstName
              case lastName
              case emailAddress
          }
      
          @State private var firstName = ""
          @State private var lastName = ""
          @State private var emailAddress = ""
          @FocusState private var focusedField: Field?
      
          var body: some View {
              ZStack {
                  VStack {
                      TextField("Enter first name", text: $firstName)
                          .focused($focusedField, equals: .firstName)
                          .textContentType(.givenName)
                          .submitLabel(.next)
                      
                      TextField("Enter last name", text: $lastName)
                          .focused($focusedField, equals: .lastName)
                          .textContentType(.familyName)
                          .submitLabel(.next)
                      
                      TextField("Enter email address", text: $emailAddress)
                          .focused($focusedField, equals: .emailAddress)
                          .textContentType(.emailAddress)
                          .submitLabel(.join)
                  }
                  .onSubmit {
                      switch focusedField {
                      case .firstName:
                          focusedField = .lastName
                      case .lastName:
                          focusedField = .emailAddress
                      default:
                          print("Creating account…")
                      }
                  }
              }
              .textFieldStyle(.roundedBorder)
              .padding()
              .frame(maxWidth: .infinity, maxHeight: .infinity)
              .contentShape(Rectangle()) // So ZStack becomes clickable
              .onTapGesture {
                  focusedField = nil
              }
          }
      }
       
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-22
        • 1970-01-01
        • 2021-05-04
        • 1970-01-01
        • 1970-01-01
        • 2020-02-20
        相关资源
        最近更新 更多