如何在 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 中?
在列表中加载 WebView 而无需重新加载 webView 内容 SwiftUI