我创建了一个带有自定义单元格的tableView,每个单元格都有一个图像。
在模型类中,我创建了一个func mainPulatesData()
来使用URLSession
dataTask方法从url中检索数据,并将数据转换为完成处理程序块中的UIImage
,然后将图像添加到UIImage
数组的变量。
检索数据并将其添加到UIImage
数组中的过程在DispatchQueue.global(qos: .userInitiated).async
块中执行。根据打印消息,图像确实被添加到数组中。
但是,即使我在tableView控制器中创建了一个模型类实例,并调用mainPulatesData()
中的viewDidlLoad
,图像也没有显示在表格中。
基于表视图控制器类中的其他打印消息,我发现它甚至可以添加到模型类中的数组中,但似乎对tableView控制器中的模型类实例不起作用。
这是获取图像数据的模型类中的代码:
func mainPulatesData() {
let session = URLSession.shared
if myURLs.count > 0{
print("\(myURLs.count) urls")
for url in myURLs{
let task = session.dataTask(with: url, completionHandler: { (data,response, error) in
let imageData = data
DispatchQueue.global(qos: .userInitiated).async {
if imageData != nil{
if let image = UIImage(data: imageData!){
self.imageList.append(image)
print("\(self.imageList.count) images added.")
}
}
else{
print("nil")
}
}
})
task.resume()
}
}
}
这是视图控制器中用于创建模型实例的代码:
override func viewDidLoad() {
super.viewDidLoad()
myModel.mainPulatesURLs()
myModel.mainPulatesData()
loadImages()
}
private func loadImages(){
if myModel.imageList.count > 0{
tableView.reloadData()
}
else{
print("data nil")
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myModel.imageList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "imageCell", for: indexPath) as! ImageTableViewCell
if myModel.imageList.count > 0{
let image = myModel.imageList[indexPath.row]
cell.tableImage = image
return cell
}
return cell
}
答案 0 :(得分:0)
您应该记下函数调用的顺序:
myModel.mainPulatesURLs() --> populates myURLs
myModel.mainPulatesData() --> populates image from myURLs in forloop asynchronously.
loadImages() --> called asynchronously.
当您从myModel.mainPulatesData()
加载已经loadImages()
myModel.imageList
loadImages()
时仍然为空的图片时,
您应该在myModel.mainPulatesData()
回调后或当您确定已加载的图片时调用dispatch_group_t
。
您可以使用import UIKit
var myURLs: [String] = ["urlA", "urlB", "urlC", "urlD"]
// we define the group for our asynchronous fetch. also works in synchronous config
var fetchGroup = DispatchGroup()
for urlString in myURLs {
// for every url fetch we define, we will call an 'enter' to issue that there is a new block for us to wait or monitor
fetchGroup.enter()
// the fetch goes here
let url = URL(string: urlString)!
URLSession.shared.downloadTask(with: URLRequest(url: url), completionHandler: { (urlReq, urlRes, error) in
// do your download config here...
// now that the block has downloaded the image, we are to notify that it is done by calling 'leave'
fetchGroup.leave()
}).resume()
}
// now this is where our config will be used.
fetchGroup.notify(queue: DispatchQueue.main) {
// reload your table here as all of the image were fetched regardless of download error.
}
配置回调。
根据要求提供:
scoped_lock
答案 1 :(得分:0)
图像不会显示,因为您在后台线程中下载(异步),并且loadImages()
被同步调用。这意味着在loadImage()
执行之前调用myModel.mainPulatesData()
,因此在下载图像时,tableview
未更新(reloadData()
未被调用)。您应创建Protocol
以通知UIViewController
,已下载数据,或使用Completion Handler
。
我正在使用的处理程序的简单示例,我在我的viewDidLoad
中调用它,它从服务器请求数据并返回[Reservation]?
的数组
ReservationTableModule.requestAllReservations { [weak self] reservations in
guard let `self` = self else {
return
}
guard let `reservations` = `reservations` else {
return
}
self.reservations = reservations
.reservationsTableView.reloadData()
}
这是实际的请求函数
class func requestAllReservations(handler: @escaping ([Reservation]?) -> Void) {
let url = "reservations/all"
APIModel.shared.requestWithLocation(.post, URL: url, parameters: nil) { data in
let reservations = data?["reservations"].to(type: Reservation.self) as? [Reservation]
handler(reservations)
}
}
handler: @escaping ([Reservation]?) -> Void
被称为完成处理程序,您应该,我想将其设为handler: @escaping ([UIImage]?) -> Void
并在您的数据下载后调用handler(reservations)
答案 2 :(得分:0)
原因是,在您imageList
之后调用[{1}}时,图片或cellForRowAt
尚未就绪。
一个好的做法是在开头使用占位符图像,只在表视图单元格可见时加载图像而不是一次加载所有内容。类似的东西:
reloadData()