我想在UITableViewCell上显示一些图像。但是我在下面收到了一个错误
fatal error: Index out of range
。问题是闭包可能不会在主线程中运行。我该如何解决这个问题?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PickupTableViewCell", for: indexPath) as! PickupTableViewCell
APIManager.getAnotherArticle{ (articles: Array<Article>?) in
for info in articles! {
self.authorArray.append(info.author)
self.descriptionArray.append(info.description)
if info.publishedAt != nil {
self.publishedAtArray.append(info.publishedAt)
}
self.titleArray.append(info.title)
self.urlArray.append(info.url)
self.urlToImageArray.append(info.urlToImage)
print(self.authorArray)
}
}
let program = urlToImageArray[indexPath.row] //index out of range
let urlToImage = NSURL(string: program)
cell.pickupImageView.sd_setImage(with: urlToImage as URL!)
return cell
}
答案 0 :(得分:3)
在DispatchQueue.main.async{ ... }
中的主队列中包装您想要运行的任何内容。
尽管如此,您当前的方法可能无法奏效。此方法称为 lot 。当用户滚动时,每当一个单元格即将出现在屏幕上时,就会调用此方法(在iOS 10中,有时会在它出现在屏幕上之前)。单元通常被回收,并且每次请求单元时,您都会将数据附加到titleArray
和其他数组(它们可能不是有序的;它们可能已经被取出;这个数组不会被卷绕正确的顺序)。
您需要将有关单元格的所有数据移动到模型对象中,并移出视图控制器。不应该有titleArray
和urlArray
等。应该只有Article
,Article
应该负责获取自己并更新其属性。此方法的工作是从缓存中获取正确的Article
,或者根据需要创建一个新的ArticleCell
,并将其分配给ArticleCell
。 Article
应Article
观看class Article {
var author: String
var description: String
var publishedAt: Date
var title: String
var url: URL
var image: UIImage
func refresh() {
// fetch data from server and replace all the placeholder data
}
}
,并在class Model {
func article(at index: Int) -> Article {
if let article = lookupArticleInCache(at: index) {
return article
}
let article = createAndCachePlaceholderArticle(at: index)
article.refresh()
}
}
更改时(即提取完成时)自行更新。几乎没有工作应该直接在这个方法中发生,因为它经常被调用,并且可能是随机命令。
构建此类事物的常用方法是使用简单的模型对象(通常是引用类型,因此可以观察到;还有许多其他方法允许结构,但它们更先进,所以我们'保持这个简单):
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PickupTableViewCell", for: indexPath) as! PickupTableViewCell
cell.article = sharedModel.article(at: indexPath.row)
return cell
}
然后有某种模型出现这些:
ArticleDelegate
然后您的代码如下:
Article
您可以使用KVO或Swift Observables或Article
协议让细胞观察using (Stream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.None))
{
MimeKit.MimeMessage mimeMessage = MimeKit.MimeMessage.Load(stream);
int i = 1;
foreach (MimeKit.MimePart attachment in mimeMessage.Attachments)
{
if (attachment.GetType() == typeof(MimeKit.Tnef.TnefPart))
{
MimeKit.Tnef.TnefPart tnefPart = (MimeKit.Tnef.TnefPart)attachment;
MimeKit.MimeMessage tnefMessage = tnefPart.ConvertToMessage();
tnefMessage.WriteTo(path + $"_tnefPart{i++}.eml");
}
}
}
。当Attachments
更新时,单元格会自行更新。
同样,有很多方法可以解决这个问题。您可以拥有所有单元格共享的“PlaceHolderArticle”,当真正的文章进入时,单元格会替换整个内容(因此文章是不可变的而不是自我更新的)。您可以使用Swift Talk描述的更通用的方法。有很多方法。但关键是这个模型可以独立于任何特定的UI更新自身,以及观察模型并显示其所拥有的UI(视图,视图控制器)。
如果您想要更多关于此主题的信息,请搜索“Massive View Controller”。这是您目前正在使用的反模式的通用名称。有很多方法可以解决这个问题,所以不要认为你在其上阅读的任何特定文章是“正确的方式”(人们已经提出了一些非常复杂,过于精细的解决方案)。但所有这些都是基于将模型与UI分离。
答案 1 :(得分:0)
APIManager.getAnotherArticle{ (articles: Array<Article>?) in
for info in articles! {
self.authorArray.append(info.author)
self.descriptionArray.append(info.description)
if info.publishedAt != nil {
self.publishedAtArray.append(info.publishedAt)
}
self.titleArray.append(info.title)
self.urlArray.append(info.url)
self.urlToImageArray.append(info.urlToImage)
print(self.authorArray)
}
}
您必须为此计算单独执行功能,并尝试避免&#34; cellForRowAt &#34;中的任何计算功能。