如何在 SwiftUI 列表中重新加载数据?

Posted

技术标签:

【中文标题】如何在 SwiftUI 列表中重新加载数据?【英文标题】:How to reload data in SwiftUI List? 【发布时间】:2020-06-29 14:45:17 【问题描述】:

我是 ios 开发新手,如果这是一个明显的问题,我很抱歉。但我不知道如何更新 SwiftUI List 中的数据。我从 API 获取数据并使用 @ObservedObject 将其传递给 ContentView。当我启动我的应用程序时它工作正常,但是在我更改我的 API 请求(通过在 SearchBar 中键入关键字)并再次获取它之后,即使数据已更改,它似乎也没有更新列表。

ContentView.swift

struct ContentView: View 

@ObservedObject var networkManager = NetworkManager()

@State var searchText: String = ""

var body: some View 
    NavigationView
        VStack 
            SearchBar(text: $searchText, placeholder: "Enter a keyword")
            List(networkManager.posts)  post in
                NavigationLink(destination: DetailView(url: post.url)) 
                    HStack 
                        Text(post.title)
                    
                
                
            .gesture(DragGesture().onChanged  _ in UIApplication.shared.endEditing() )
            
        .navigationBarTitle("News")
        
    
    .onAppear 
        self.networkManager.fetchData(self.searchText)
    
    


NetworkManager.swift

class NetworkManager: ObservableObject 



@Published var posts = [Post]()

func fetchData(_ keyword: String?)
    var urlString = "https://newsapi.org/v2/top-headlines?country=us&apiKey=5dcef32f4c69413e8fe128cc5c7ba4cf"
    if keyword != nil 
        urlString = "https://newsapi.org/v2/top-headlines?country=us&apiKey=5dcef32f4c69413e8fe128cc5c7ba4cf&q=\(keyword!)"
    
    print(urlString)
    if let url = URL(string: urlString)
        let session = URLSession(configuration: .default)
        let task = session.dataTask(with: url)  (data, response, error) in
            if error == nil
                let decoder = JSONDecoder()
                if let safeData = data
                    do
                        let results = try decoder.decode(News.self, from: safeData)
                        DispatchQueue.main.async 
                            self.posts = results.articles
                            print(self.posts)
                        
                     catch
                        print(error)
                    
                
            
        
        task.resume()
    
    



SearchBar.swift(我在 searchBarSearchButtonClicked 中再次获取数据)

struct SearchBar: UIViewRepresentable 
    
    
    @Binding var text: String
    var placeholder: String
    
    class Coordinator: NSObject, UISearchBarDelegate 
       
        @ObservedObject var networkManager = NetworkManager()
        
        @Binding var text: String
        
        init(text: Binding<String>) 
            _text = text
        
        
        func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) 
            text = searchText
        
        
        func searchBarSearchButtonClicked(_ searchBar: UISearchBar) 
            print(text)
            DispatchQueue.main.async 
                self.networkManager.fetchData(self.text)
            
            
            UIApplication.shared.endEditing()
            
        
    
    
    func makeCoordinator() -> SearchBar.Coordinator 
        return Coordinator(text: $text)
    
    
    func makeUIView(context: UIViewRepresentableContext<SearchBar>) -> UISearchBar 
        let searchBar = UISearchBar(frame: .zero)
        searchBar.delegate = context.coordinator
        searchBar.placeholder = placeholder
        searchBar.searchBarStyle = .minimal
        searchBar.autocapitalizationType = .none
        return searchBar
    
    
    func updateUIView(_ uiView: UISearchBar, context: UIViewRepresentableContext<SearchBar>) 
        uiView.text = text
    
    


extension UIApplication 
    func endEditing() 
        sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    

News.swift

struct News: Decodable 
    let articles: [Post]


struct Post: Decodable, Identifiable 
    var id: String
        return url!
    
    let title: String
    let url: String?

【问题讨论】:

解码后尝试调用objectWillChange.send()。题外话,但我认为您可以删除DispatchQueue.main.async 部分并直接分配self.post @JoakimDanielson 我在self.posts = results.articles 之后添加了objectWillChange.send(),但它没有用。关于 DispatchQueue,当我删除它时,Xcode 说:“不允许从后台线程发布更改” 【参考方案1】:

我做了一些小的修改,使代码可以在 Xcode-playgrounds 中运行。方法如下:

型号:

struct News: Codable  var articles: [Post] 
struct Post: Identifiable, Codable  var title: String; var id: String  title  

内容视图:

struct ContentView: View 

    @ObservedObject var networkManager = NetworkManager()

    var body: some View 
        NavigationView 
            VStack 
                TextField("Enter a keyword", text: $networkManager.searchText)
                List(networkManager.posts)  post in
                    NavigationLink(destination: EmptyView()) 
                        HStack 
                            Text(post.title)
                        
                    
                
            .navigationBarTitle("News")
        
        .onAppear 
            self.networkManager.fetchData()
        

    

网络管理器:

class NetworkManager: ObservableObject 

    @Published var searchText: String = "" 
        didSet 
            fetchData()
        
    
    @Published var posts = [Post]()

    func fetchData() 
        let urlString = "https://newsapi.org/v2/top-headlines?country=us&apiKey=5dcef32f4c69413e8fe128cc5c7ba4cf&q=\(searchText)"
        if let url = URL(string: urlString)
            let session = URLSession(configuration: .default)
            let task = session.dataTask(with: url)  (data, response, error) in
                if error == nil
                    let decoder = JSONDecoder()
                    if let safeData = data
                        do
                            let results = try decoder.decode(News.self, from: safeData)
                            DispatchQueue.main.async 
                                self.posts = results.articles
                                print(self.posts)
                            
                         catch
                            print(error)
                        
                    
                
            
            task.resume()
        
    

【讨论】:

检查更新。我在 Xcode 游乐场测试过。 我已经编辑了我的问题,添加了 SearchBar.swift。您能否检查并告诉我如何将它与 SearchBar 而不是 TextField 一起使用,就像您在示例中使用的那样?当你有时间。非常感谢! SearchBar修改成TextField一样接受绑定文本和标题的相同参数。 SearchBar 应该只需要处理 View 部分。

以上是关于如何在 SwiftUI 列表中重新加载数据?的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 列表 - 远程图像加载 = 图像闪烁(重新加载)

如何将 UIImage 异步加载到 SwiftUI Image 中?

在 SwiftUI 列表中重新加载单个行属性

在列表中加载 WebView 而无需重新加载 webView 内容 SwiftUI

如何允许对不在编辑模式下的 SwiftUI 列表中的行进行重新排序?

如何在 SwiftUI 列表中设置 NavigationLink