如何让 TextField 值更改触发 SwiftUI 中另一条数据的更新?

Posted

技术标签:

【中文标题】如何让 TextField 值更改触发 SwiftUI 中另一条数据的更新?【英文标题】:How do you have a TextField value change trigger an update to another piece of data in SwiftUI? 【发布时间】:2019-10-10 22:31:11 【问题描述】:

我正在尝试学习 Swift、SwiftUI 和 Combine,总的来说,我是 ios 新手。最终我想要一个可以搜索、过滤和排序的列表。

到目前为止,当我在 TextField 中使用 onEditingChanged 时,我的过滤工作正常,但这需要按 Enter。我只是不知道如何在 TextField filterText 更改时触发 activePeople 进行更新,以便 activePeople 列表过滤同时您键入。

当我在视图中的 ForEach 中过滤列表时,我已经获得了一个可以工作的版本(请参阅注释掉的代码),但最终过滤和排序会变得更加复杂,并且将它放在外部似乎更有意义那个观点。如果出于某种原因这不是正确的方法,请告诉我。

到目前为止的代码如下:

import Combine
import SwiftUI

class Model: ObservableObject 
    @Published var filterText: String = “”
    @Published var activePeople: [Person] = []

    private var allPeople : [Person] = [
        Person( id: 1000, name: “Alexa” ),
        Person( id: 1001, name: “Anaïs” ),
        Person( id: 1002, name: “Earl” ),
        Person( id: 1003, name: “Elba” ),
        Person( id: 1004, name: "Emil” ),
        Person( id: 1005, name: “Janeth” ),
        Person( id: 1006, name: “Joselyn” ),
        Person( id: 1007, name: “Lupita” ),
        Person( id: 1008, name: “Mellie” ),
        Person( id: 1009, name: “Vanita” ),
    ]

    init() 
        activePeople = allPeople
    

    func filterList() 
        if ( filterText == “” ) 
            activePeople = allPeople
         else 
            activePeople = allPeople.filter(  $0.name.localizedStandardContains( filterText )  )
        
    



struct Person: Identifiable 
    var id: Int
    var name: String


import SwiftUI

struct ContentView: View 
    @EnvironmentObject private var model: Model

    var body: some View 
        VStack 
            Form 
                Section 
                    TextField(“Filter Text”, text: $model.filterText, onEditingChanged: _ in self.model.filterList()
                    )
                
                Section 
                    Text( "Filtered by: \(model.filterText)” )
                    ForEach( model.activePeople )  person in
//                        if ( self.model.filterText == “” || person.name.localizedStandardContains( self.model.filterText )) 
                            Text( person.name )
//                        
                    
                
            
        
    


struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
            .environmentObject(Model())
    



【问题讨论】:

【参考方案1】:

为了观察文本字段输入的变化,您可以观察模型中的变量。从视图中删除过滤逻辑可能也是一个好主意。 这方面的一个例子是在模型初始化时开始观察filterText。另外,请记住保留对从sink 返回的可取消对象的引用。 我们在这里所做的是每次来自 filterText 的值发生变化(并且由于文本字段绑定而发生变化),我们将过滤数组并将值分配给 activePeople 变量

 var filterCancellable: AnyCancellable?

    init() 
        filterCancellable = $filterText.sink [weak self] currentText in
            guard let strongSelf = self else  return  
            strongSelf.activePeople = strongSelf.filterList(with: currentText)
        
    

    func filterList(with text: String) -> [Person] 
        if ( text == "" ) 
            return allPeople
         else 
            return allPeople.filter(  $0.name.localizedStandardContains( text )  )
        
    

这样你以后就可以这样做了

var body: some View 
        VStack 
            Form 
                Section 
                    TextField("Filter Text", text: $model.filterText)
                
                Section 
                    Text( "Filtered by: \(model.filterText)" )
                    ForEach( model.activePeople )  person in
                        Text( person.name )
                    
                
            
        
    

【讨论】:

【参考方案2】:

只需在您的 ContentView 中应用过滤:

ForEach(model.activePeople.filter( self.model.filterText.isEmpty ||
                                   $0.name.localizedStandardContains(self.model.filterText)))  person in
                            Text( person.name )
                    

【讨论】:

有没有办法让这个不被看到?最终我想使用多个过滤器并应用一个排序。是否按照 SwiftUI 的工作方式将所有这些逻辑保留在视图中? 您可以将其移至负责过滤数据的单独结构中。

以上是关于如何让 TextField 值更改触发 SwiftUI 中另一条数据的更新?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 中更改键盘完成按钮上 Textfield 的文本?

Swift inout 如何在未更改时不复制回属性,以不触发对象设置器

Swift 如何在另一个 UITextField 突出显示或具有值时禁用 TextField

我如何在SWIFTUI中把textField作为firstResponder,就像Swift一样:textField.becomesFirstResponder。

如何从 From 外部更改 Formik TextField 值?

如何更改 TextField 中的字段大小?