SwiftUI 结合去抖动 TextField [英] SwiftUI Combine Debounce TextField

查看:36
本文介绍了SwiftUI 结合去抖动 TextField的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有 SwiftUI 应用程序生命周期的 SwiftUI 应用程序.我正在尝试设置一种标准的添加方式将 debounce 输入到 TextFields.理想情况下,我想创建自己的 TextField 修饰符可以很容易地应用于有许多文本字段要编辑的视图.我试过一堆方法来做到这一点,但我必须错过一些基本的东西.这是一个例子.这个不起作用:

I have a SwiftUI app with SwiftUI App life cycle. I'm trying to setup a standard way to add typing debounce to TextFields. Ideally, I'd like to create my own TextField modifier that can easily be applied to views that have many textfields to edit. I've tried a bunch of ways to do this but I must be missing something fundamental. Here's one example. This does not work:

struct ContentView: View {

    @State private var searchText = ""
    
    var body: some View {
    
        VStack {
            Text("You entered: \(searchText)")
                .padding()
            TextField("Enter Something", text: $searchText)
                .frame(height: 30)
                .padding(.leading, 5)
                .overlay(
                    RoundedRectangle(cornerRadius: 6)
                        .stroke(Color.blue, lineWidth: 1)
                )
                .padding(.horizontal, 20)
                .onChange(of: searchText, perform: { _ in
                    var subscriptions = Set<AnyCancellable>()
                
                    let pub = PassthroughSubject<String, Never>()
                    pub
                        .debounce(for: .seconds(1), scheduler: DispatchQueue.main)
                        .collect()
                        .sink(receiveValue: { t in
                            self.searchText = t.first ?? "nothing"
                        } )
                        .store(in: &subscriptions)
                })
        }
    }
}

任何指导将不胜感激.Xcode 12.4,iOS 14.4

Any guidance would be appreciated. Xcode 12.4, iOS 14.4

推荐答案

我认为您必须保留两个变量:一个用于用户键入时字段中的文本,另一个用于去抖动文本.否则,用户将不会看到实时输入,我假设这不是您想要的行为.我猜这可能适用于更标准的用例,例如,在用户暂停输入后执行数据提取.

I think you'll have to keep two variables: one for the text in the field as the user is typing and one for the debounced text. Otherwise, the user wouldn't see the typing coming in in real-time, which I'm assuming isn't the behavior you want. I'm guessing this is probably for the more standard use case of, say, performing a data fetch once the user has paused their typing.

我喜欢 ObservableObjects 和 Combine 来管理这类事情:

I like ObservableObjects and Combine to manage this sort of thing:

class TextFieldObserver : ObservableObject {
    @Published var debouncedText = ""
    @Published var searchText = ""
    
    private var subscriptions = Set<AnyCancellable>()
    
    init() {
        $searchText
            .debounce(for: .seconds(1), scheduler: DispatchQueue.main)
            .sink(receiveValue: { t in
                self.debouncedText = t
            } )
            .store(in: &subscriptions)
    }
}

struct ContentView: View {
    @StateObject var textObserver = TextFieldObserver()
    
    @State var customText = ""
    
    var body: some View {
    
        VStack {
            Text("You entered: \(textObserver.debouncedText)")
                .padding()
            TextField("Enter Something", text: $textObserver.searchText)
                .frame(height: 30)
                .padding(.leading, 5)
                .overlay(
                    RoundedRectangle(cornerRadius: 6)
                        .stroke(Color.blue, lineWidth: 1)
                )
                .padding(.horizontal, 20)
            Divider()
            Text(customText)
            TextFieldWithDebounce(debouncedText: $customText)
        }
    }
}

struct TextFieldWithDebounce : View {
    @Binding var debouncedText : String
    @StateObject private var textObserver = TextFieldObserver()
    
    var body: some View {
    
        VStack {
            TextField("Enter Something", text: $textObserver.searchText)
                .frame(height: 30)
                .padding(.leading, 5)
                .overlay(
                    RoundedRectangle(cornerRadius: 6)
                        .stroke(Color.blue, lineWidth: 1)
                )
                .padding(.horizontal, 20)
        }.onReceive(textObserver.$debouncedText) { (val) in
            debouncedText = val
        }
    }
}

我包含了两个示例 - 顶部,其中容器视图 (ContentView) 拥有 ObservableObject 和底部,将其制成更可重用的组件.

I included two examples -- the top, where the container view (ContentView) owns the ObservableObject and the bottom, where it's made into a more-reusable component.

这篇关于SwiftUI 结合去抖动 TextField的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆