我有一个相当普遍的问题,但到目前为止解决方案还没有给我预期的结果。
我有一个UITableView
,我填充的信息是我在运行时从网络上提取的JSON中解析的。 JSON检索使用func viewDidLoad()
函数在NSURLSession.dataTaskWithURL(NSURL, completionHandler)
中开始。然后,表格的单元格填充在completionHandler
。
表中使用的单元格是自定义的,单元格中的所有UI元素都具有通过界面构建器设置的默认值。
当表出现时,它完全是空的,尽管它具有正确的单元格高度。我发现这种行为的原因是reloadData
函数由于多线程/多处理而没有在正确的时间调用,建议的解决方案是
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
代码中的某处允许重新加载正确发生。但是,这只是部分有效。表格看起来像内容一样闪烁,然后立即再次变为空白,直到我滚动。一旦我滚动reloadData
函数按预期工作,但只有在我滚动之后。
我已尝试在不同位置(例如reloadData
)中使用viewWillAppear
功能而没有运气。我可以尝试任何想法或疑难解答提示吗?
编辑#1 - 请求完成处理程序
var listTask = session.dataTaskWithURL(listUrl, completionHandler: {(data, response, error) in
if error == nil {
var json:NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: nil) as NSDictionary
var topGames = json["top"] as [NSDictionary]
var currGame:NSDictionary
var toAdd:Game
for var i=0; i < topGames.count; ++i {
currGame = topGames[i]["game"] as NSDictionary
toAdd = Game(
id: String(currGame["_id"] as CLong),
name: currGame["name"] as NSString,
boxArtImageUrl: (currGame["box"] as NSDictionary)["medium"] as NSString,
boxArtImage: nil,
isFetchingBoxArt: false,
totalViewers: topGames[i]["viewers"] as Int,
totalChannels: topGames[i]["channels"] as Int,
topChannel: "N/A")
self.games.append(toAdd)
}
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
}
}
请注意,func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCel
中使用了self.games数组来填充单元格中的信息。
编辑#2 - 申请cellForRowAtIndexPath
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:TopGameListCell = self.tableView.dequeueReusableCellWithIdentifier("topGameCell") as TopGameListCell
if games.count > 0 {
var game:Game = games[indexPath.row]
cell.nameLabel?.text = game.name
cell.totalViewersLabel.text = String(game.totalViewers)
cell.totalChannelsLabel.text = String(game.totalChannels)
cell.topChannelLabel.text = game.topChannel
if game.boxArtImage != nil {
cell.boxArtImageView.image = game.boxArtImage
} else {
if(!game.isFetchingBoxArt) {
cell.boxArtImageView.image = placeholderImage
gatherGameBoxArtImageForCell(game.boxArtImageUrl, indexPath: indexPath)
}
}
}
return cell;
}
我忘记了我在gatherGameBoxArtImageForCell
内开始的额外提取。基本上如果我没有图像,请下载它。图像的if / else似乎导致闪烁发生。如果我让它坐得足够长,它最终会显示出来。我的想法是占位符图像会显示,直到下载图像,然后在下载图像后调用reloadData
。这是图片提取completionHandler
:
var task = session.dataTaskWithURL(url, completionHandler: {(data, response, error) in
var game = self.games[indexPath.row]
if error == nil {
if(game.boxArtImage == nil) {
var cellForImage:TopGameListCell? = self.tableView.cellForRowAtIndexPath(indexPath) as? TopGameListCell
if cellForImage != nil {
self.games[indexPath.row].boxArtImage = UIImage(data: data)
cellForImage?.boxArtImageView.image = self.games[indexPath.row].boxArtImage
}
}
} else {
println(error)
}
game.isFetchingBoxArt = false
self.tableView.reloadData()
})
答案 0 :(得分:0)
看来,当我试图让图像在后台加载时,它正在占据主线程。我所做的是从
改变了以下内容if(!game.isFetchingBoxArt) {
cell.boxArtImageView.image = placeholderImage
gatherGameBoxArtImageForCell(game.boxArtImageUrl, indexPath: indexPath)
}
到
if(!game.isFetchingBoxArt) {
cell.boxArtImageView.image = placeholderImage
dispatch_async(dispatch_get_main_queue(), {
self.gatherGameBoxArtImageForCell(game.boxArtImageUrl, indexPath: indexPath)
})
}
在考虑了reloadData
dispatch_async
的正常解决方案后,我决定这样做。显然我的问题的推理是相同的,所以现在下载的图像是异步的。