SwiftUI - 滚动时关闭键盘

18

我有一个简单的搜索列表:


struct ContentView: View {
    @State var text:String = ""
    var items = 1...100
    var body: some View {
        VStack {
            List {
                TextField("Search", text: $text)
                Section{
                    ForEach(items.filter({"\($0)".contains(text)}),id: \.self){(i) in
                       Text("option \(i)")
                    }
                }
            }
        }
    }
}

iOS simulator screenshot

如何在滚动超过2个单元格/几个点时关闭键盘?

6个回答

30
如果您正在使用 ScrollView(可能还带有List,但我没有确认),您可以使用 UIScrollView appearance,这将影响所有的 ScrollViews。
UIScrollView.appearance().keyboardDismissMode = .onDrag

到目前为止,这是最好的建议。同时,在同一滚动视图上使用其他视图/组件(如“滑块”)也能很好地工作。 - Juraj Blahunka
这适用于List,我已经测试过了,可能也适用于其他可滚动视图。我猜这是因为ListScrollView的子类。 - qsmy
5
如果显示了表情符号键盘并滑动表情符号,则会导致键盘奇怪地隐藏 - 对于如何修复这个问题有什么想法吗? - hyouuu
1
只有在没有任何可以滚动的文本编辑器或文本视图时才使用此通用配置,因为它们也会受到影响。 - Teffi

11

截至目前,自iOS 16测试版开始,我们有了一个新的修饰符scrollDismissesKeyboard(),可以完全满足您的需求。

在您的示例中应该如下所示

struct ContentView: View {
    @State var text: String = ""
    var items = 1...100
    var body: some View {
        List {
            TextField("Search", text: $text)
            Section {
                ForEach(items.filter({"\($0)".contains(text)}), id: \.self) { (i) in
                    Text("option \(i)")
                }
            }
        }
        .scrollDismissesKeyboard(.interactively) // <<-- Put this line
    }
}

scrollDismissesKeyboard() 修改器有一个参数,用于确定键盘退出规则。以下是可能的取值:

  • .automatic:基于滚动上下文来取消键盘。
  • .immediately:任何滚动发生时立即关闭键盘。
  • .interactively:键盘将随用户手势移动/消失。
  • .never:用户滚动时键盘永远不会消失。

11
可以在这个问题中找到关于如何使用不同答案撤销键盘的全面讨论。
在列表中使用拖动手势撤销键盘的一种解决方案是使用UIApplication窗口上的一个方法,如下所示。为了更容易处理,我创建了一个UIApplication扩展和视图修饰符的扩展,并最终创建了一个View扩展:
extension UIApplication {
    func endEditing(_ force: Bool) {
        self.windows
            .filter{$0.isKeyWindow}
            .first?
            .endEditing(force)
    }
}

struct ResignKeyboardOnDragGesture: ViewModifier {
    var gesture = DragGesture().onChanged{_ in
        UIApplication.shared.endEditing(true)
    }
    func body(content: Content) -> some View {
        content.gesture(gesture)
    }
}

extension View {
    func resignKeyboardOnDragGesture() -> some View {
        return modifier(ResignKeyboardOnDragGesture())
    }
}

所以,最终为了放弃键盘而使用的修饰符只需要在列表中放置一个修饰符就可以,格式如下:
List {
    ForEach(...) {
        //...
    }
}
.resignKeyboardOnDragGesture()

我还实现了一个纯SwiftUI版本的搜索栏,可能对你很有趣。你可以在这个回答中找到它。


我不知道这个问题是否仍在积极监控中,但对于iOS 15,我收到了一个警告,提示说“'windows'在iOS 15.0中已被弃用:请改用相关窗口场景上的UIWindowScene.windows”。为iOS 15添加一个代码部分会很有帮助。 - PipEvangelist
我在这里找到了一个克服iOS 15中弃用的可能性,即使用以下代码:let scenes = UIApplication.shared.connectedScenes let windowScene = scenes.first as? UIWindowScene let window = windowScene?.windows.first window?.endEditing(force) - user3687284
但是我的解决方案存在一个问题,即在导航视图上按下返回按钮需要稍微滑动才能生效。虽然它可以工作,但有时会让人感到烦恼。与此同时,我测试了上面提到的来自vauxhaull的解决方案,它似乎可以很好地解决这个问题。 - user3687284

7
Form {
    ...
}.gesture(DragGesture().onChanged { _ in
    UIApplication.shared.windows.forEach { $0.endEditing(false) }
})

3
很遗憾,我发现这会干扰滑动删除的功能。 - C6Silver
1
同时使用 Slider 组件。 - Juraj Blahunka
奇怪的是,对我来说它只能水平工作。 - undefined

4

@FocusState包装器与.focused() TextField修饰符结合使用时非常有用。

struct ContentView: View {
    @FocusState private var focusedSearchField: Bool
    @State var text:String = ""
    var items = 1...100
    var body: some View {
        VStack {
            List {
                TextField("Search", text: $text)
                    .focused($focusedSearchField)
                Section{
                    ForEach(items.filter({"\($0)".contains(text)}),id: \.self){(i) in
                        Text("option \(i)")
                    }
                }
            } // to also allow swipes on items (theoretically)
            .simultaneousGesture(DragGesture().onChanged({ _ in
                focusedSearchField = false
            }))
            .onTapGesture { // dissmis on tap as well
                focusedSearchField = false
            }
        }
    }
}

1
struct EndEditingKeyboardOnDragGesture: ViewModifier {
    func body(content: Content) -> some View {
        content.highPriorityGesture (
            DragGesture().onChanged { _ in 
                UIApplication.shared.endEditing()
            }
        )
    }
}

extension View {
    func endEditingKeyboardOnDragGesture() -> some View {
        return modifier(EndEditingKeyboardOnDragGesture())
    }
}

不幸的是,它会干扰滑动手势。 - titusmagnus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接