SwiftUI 中的动态列表从 JSON 和 Textfield 更新

Posted

技术标签:

【中文标题】SwiftUI 中的动态列表从 JSON 和 Textfield 更新【英文标题】:Dynamic List in SwiftUI updated from JSON and Textfield 【发布时间】:2020-02-08 15:29:57 【问题描述】:

这是我在论坛上的第一条消息,我希望能正确发布。

我正在尝试在 SwiftUI 中创建一个动态列表,只要用户在文本字段中键入内容,它就会立即更新。

该列表使用了来自意大利火车公司的服务 viaggiatreno.it 的 API。

我将使用的特定链接返回以提供给特定 URL 的字符串开头的火车站列表。

我创建了一个 Station Class,如下所示:

struct Station: Decodable, Identifiable 
    var iid = UUID()
    var name : String
    var id: String

还有一个 StationFetcher 类,它获取使用字符串初始化的 API url,该字符串是用户将从文本字段传递的字符串:

import Foundation

public class StationFetcher : Decodable, ObservableObject 

    var stations = [Station]()
    var search = ""

    init(search: String) 
        getJsonData(string: search)
    

    func getJsonData(string: String) 

        let url = URL(string: "http://www.viaggiatreno.it/viaggiatrenonew/resteasy/viaggiatreno/cercaStazione/" + string) 
//string is the initial string of the station
            let task = URLSession.shared.dataTask(with: url!)  (data, response, error) in
                if error != nil 
                    print(error!)
                 else 
                    if let urlContent = data 
                        do 
                            let jsonResult = try JSONSerialization.jsonObject(with: urlContent , options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
                            for i in 0..<jsonResult.count 
                                if let station = jsonResult[i] as AnyObject? 
                                    if let nameStation = station["nomeLungo"] as! String? 
                                        if let idStation = station["id"] as! String? 
                                            let searchItem = Station(name: nameStation, id: idStation)
                                            DispatchQueue.main.async 
                                                self.stations.append(searchItem)
                                            
                                        
                                     else 
                                        print("error catching dictionary value")
                                    
                                
                            
                         catch 
                            print("JSON Processing failed")

                        
                    
                

            
            task.resume()
        
    

如何在 SwiftUI 主视图中管理这个?

import SwiftUI

struct Departure: View 
    @State public var selectedStation = ""
    @State private var departureDate = Date()
    @ObservedObject var fetcher = StationFetcher(search: "")

    var body: some View 
        NavigationView 
            Form 
                Section(header: Text("Cerca Stazione di partenza:")) 
                    TextField("Da dove parti?...", text: $selectedStation)
                
                Section(header: Text("Orario:")) 
                    DatePicker(selection: $departureDate) 
                        Text("Partenza")
                    
                
                Section(header: Text("Lista stazioni"))
                    List(fetcher.stations)  station in
                        Text(station.name)
                    
                
            
        .navigationBarTitle("Partenza")
        
    

谢谢大家

【问题讨论】:

你能解释一下你的意思“我怎样才能在主 SwiftUI 视图中管理这个?你当前的问题是什么?如果它不起作用,最好包含错误。如果它起作用,您认为应该改进哪些方面? 亲爱的 Bart,我实际上并没有在出发视图中加载项目列表,之后,每次我在文本字段中写入内容时,我都需要使其可加载,因为文本字段中的输入将具有与 JSON 不同的结果 【参考方案1】:

感谢您提供的帮助。我已经部分成功地完成了我正在寻找的事情,一件事的一部分。

通过在 StationFetcher 中使用 ObservableObject 协议,@Published 到我想更新的变量,我已经更新了列表。

struct Departure: View 
    @State public var selectedStation = ""
    @State private var departureDate = Date()
    @ObservedObject var fetcher = StationFetcher(search: "")

    var body: some View 
        NavigationView 
            Form 
                Section(header: Text("Cerca Stazione di partenza:")) 
                    TextField("Da dove parti?...",
                              text: $selectedStation, onEditingChanged:  _ in
                                self.fetcher.getJsonData(string: self.selectedStation)
                    )
                
                Section(header: Text("Orario:")) 
                    DatePicker(selection: $departureDate) 
                        Text("Partenza")
                    
                
                Section(header: Text("Lista stazioni"))
                    List(fetcher.stations)  station in
                        Text(station.name)
                            .autocapitalization(.words)
                    
                
            
        .navigationBarTitle("Partenza")
        
    

StationFetcher 的代码现在是:

import Foundation

public class StationFetcher : ObservableObject 

    @Published var stations = [Station]()


    init(search: String) 
        getJsonData(string: search)
    

    func getJsonData(string: String) 
        stations.removeAll(keepingCapacity: false)
        let url = URL(string: "http://www.viaggiatreno.it/viaggiatrenonew/resteasy/viaggiatreno/cercaStazione/" + string.replacingOccurrences(of: " ", with: "%20"))
//string is the initial string of the station name
            let task = URLSession.shared.dataTask(with: url!)  (data, response, error) in
                if error != nil 
                    print(error!)
                 else 
                    if let urlContent = data 
                        do 
                            let jsonResult = try JSONSerialization.jsonObject(with: urlContent , options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
                            for i in 0..<jsonResult.count 
                                if let station = jsonResult[i] as AnyObject? 
                                    if let nameStation = station["nomeLungo"] as! String? 
                                        if let idStation = station["id"] as! String? 
                                            let searchItem = Station(name: nameStation, id: idStation)
                                            DispatchQueue.main.async 
                                                self.stations.append(searchItem)
                                                print(self.stations.count)
                                            
                                        
                                     else 
                                        print("error catching dictionary value")
                                    
                                
                            
                         catch 
                            print("JSON Processing failed")

                        
                    
                

            
            task.resume()
        
    

我唯一没有工作的是列表的实时更新,只要一个字符被数字化。 现在,只要我在文本字段外单击或按回车键,列表就会更新。

我不确定 onEditingChanged 是否是正确的选项。

有人有想法吗?

非常感谢

【讨论】:

【参考方案2】:

安东尼奥。您是说要根据用户的搜索输入过滤电台列表吗?我建议进行一些小的更改来实现这一点。

通常在 ObservableObject 中,您可以将 @Published 添加到实例变量中,以使视图在更改时自动更新(例如 @Published var stations = [Station]())。但是,因为 StationFetcher 类也符合 Decodable,所以必须自己实现这个发布,可以通过在对象的发布者上调用 send() 来完成:

var stations = [Station]() 
    didSet 
        self.objectWillChange.send()
    

因此,在您的 Departure 视图中,您只需要在用户更改搜索字段时触发新的搜索。这可以通过 TextField 的可选 onEditingChanged 参数来完成,以便在用户键入新文本时执行关闭:

TextField("Da dove parti?...",
          text: $selectedStation,
          onEditingChanged:  _ in
            self.fetcher.getJsonData(string: self.selectedStation)
)

让我知道这是否适合你!

【讨论】:

@Antonio85It 嗯,函数被调用了吗?也许将您的 getJsonData 实现与虚拟(非网络)响应交换以查看该部分是否正常工作?另一种方法可能是将 didSet 添加到 selectedStation 谢谢本。它似乎工作但不完全。它会更新“站”的数组,但不会立即重新加载列表。我在 UIKit 中使用 table.reloadData() 管理的内容不起作用。

以上是关于SwiftUI 中的动态列表从 JSON 和 Textfield 更新的主要内容,如果未能解决你的问题,请参考以下文章

自学IOS开发第3天·基础SwiftUI之动态滑动列表(上)

自学IOS开发第3天·基础SwiftUI之动态滑动列表(上)

自学IOS开发第3天·基础SwiftUI之动态滑动列表(上)

SwiftUI:从列表中的项目获取切换状态

从公共 API 获取 JSON 以在 SwiftUI 的列表视图中呈现

JSON 文件中的 SwiftUI 搜索列表