UITableViewCell 异步加载图像问题 - Swift

2024-03-15

在我的应用程序中,我构建了自己的异步图像加载类。我传入一个对象,然后它检查缓存(NSCache)是否有图像,如果没有,它将检查文件系统是否已保存图像。如果图像尚未保存,它将在后台下载图像(NSOperations 帮助)。

到目前为止,效果很好,但我在表视图加载图像时遇到了一些小问题。

首先,这是我用来设置表格视图单元格的函数tableView(tableView:, willDisplayCell:, forRowAtIndexPath:)

func configureCell(cell: ShowTableViewCell, indexPath: NSIndexPath) {

    // Configure cell
    if let show = dataSource.showFromIndexPath(indexPath) {

        ImageManager.sharedManager.getImageForShow(show, completionHandler: { (image) -> Void in
            if self.indexPathsForFadedInImages.indexOf(indexPath) == nil {
                self.indexPathsForFadedInImages.append(indexPath)

                if let fetchCell = self.tableView.cellForRowAtIndexPath(indexPath) as? ShowTableViewCell {
                    func fadeInImage() {
                        // Fade in image
                        fetchCell.backgroundImageView!.alpha = 0.0
                        fetchCell.backgroundImage = image
                        UIView.animateWithDuration(showImageAnimationSpeed, animations: { () -> Void in
                            fetchCell.backgroundImageView!.alpha = 1.0
                        })
                    }

                    if #available(iOS 9, *) {
                        if NSProcessInfo.processInfo().lowPowerModeEnabled {
                            fetchCell.backgroundImage = image
                        }
                        else {
                            fadeInImage()
                        }
                    }
                    else {
                        fadeInImage()
                    }
                }
                else {
                    // Issues are here
                }
            }
            else {
                // Set image
                cell.backgroundImage = image
            }
        })
...
}

在“// Issues are here”注释所在的位置,这就是我遇到多个问题的地方。

到目前为止,我还没有找到另一种方法来验证图像是否属于单元格,以确定“//问题在这里”在哪里。如果我添加

cell.backgroundImage = 图像

在那里,它修复了有时图像不会显示在表格视图单元格上的问题。到目前为止,我发现的唯一原因是图像的返回速度比我返回表视图单元格的速度快,这就是为什么表视图说该索引路径处没有单元格的原因。

但如果我在那里添加该代码,那么我会遇到另一个问题!单元格将显示错误的图像,然后它会滞后于应用程序,并且图像会不断切换,甚至只是停留在错误的图像上。

我检查过它在主线程上运行,图像下载和缓存一切正常。它只需要表说该索引路径上没有单元格,并且我尝试获取也返回 nil 的单元格的索引路径。

这个问题的半解决方案是在 viewWillAppear/viewDidAppear 中调用 tableView.reloadData() 。这将解决问题,但随后我会丢失屏幕上表格视图单元格的动画。

EDIT:

如果我将图像视图传递给 getImageForShow() 并直接设置它,它将解决这个问题,但这不是理想的代码设计。图像视图显然存在,单元格存在,但由于某种原因它不想每次都工作。


表视图重用单元格以节省内存,这可能会导致需要执行以显示单元格数据的任何异步例程(例如加载图像)出现问题。如果异步操作完成时单元格应该显示不同的数据,则应用程序可能会突然进入不一致的显示状态。

为了解决这个问题,我建议向单元格添加一个生成属性,并在异步操作完成时检查该属性:

protocol MyImageManager {
    static var sharedManager: MyImageManager { get }
    func getImageForUrl(url: String, completion: (UIImage?, NSError?) -> Void)
}

struct MyCellData {
    let url: String
}

class MyTableViewCell: UITableViewCell {

    // The generation will tell us which iteration of the cell we're working with
    var generation: Int = 0

    override func prepareForReuse() {
        super.prepareForReuse()
        // Increment the generation when the cell is recycled
        self.generation++
        self.data = nil
    }

    var data: MyCellData? {
        didSet {
            // Reset the display state
            self.imageView?.image = nil
            self.imageView?.alpha = 0
            if let data = self.data {
                // Remember what generation the cell is on
                var generation = self.generation
                // In case the image retrieval takes a long time and the cell should be destroyed because the user navigates away, make a weak reference
                weak var wcell = self
                // Retrieve the image from the server (or from the local cache)
                MyImageManager.sharedManager.getImageForUrl(data.url, completion: { (image, error) -> Void in
                    if let error = error {
                        println("There was a problem fetching the image")
                    } else if let cell = wcell, image = image where cell.generation == generation {
                        // Make sure that UI updates happen on main thread
                        dispatch_async(dispatch_get_main_queue(), { () -> Void in
                            // Only update the cell if the generation value matches what it was prior to fetching the image
                            cell.imageView?.image = image
                            cell.imageView?.alpha = 0
                            UIView.animateWithDuration(0.25, animations: { () -> Void in
                                cell.imageView?.alpha = 1
                            })
                        })
                    }
                })
            }
        }
    }
}

class MyTableViewController: UITableViewController {

    var rows: [MyCellData] = []

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCellWithIdentifier("Identifier") as! MyTableViewCell
        cell.data = self.rows[indexPath.row]
        return cell
    }

}

其他一些注意事项:

  • 不要忘记在主线程上进行显示更新。在网络活动线程上更新可能会导致显示在看似随机的时间发生变化(或从不发生变化)
  • 当您执行异步操作时,请务必弱引用单元格(或任何其他 UI 元素),以防 UI 在异步操作完成之前被销毁。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UITableViewCell 异步加载图像问题 - Swift 的相关文章

  • 来自索引范围 Swift 的新数组

    我怎样才能做这样的事情 从数组中取出前 n 个元素 newNumbers numbers 0 n 目前出现以下错误 error could not find an overload for subscript that accepts th
  • 为什么等待冷任务不会抛出

    我只是在尝试看看当一个冷任务 即一个任务 时会发生什么Task尚未开始 正在等待 令我惊讶的是 代码永远挂起并且 完成的 永远不会被打印 我希望抛出异常 public async Task Test1 var task new Task g
  • 以异步方式执行 Express res.render

    我有一个 Nodejs 应用程序 其中res renderExpress 方法以阻塞方式花费大约 400 毫秒 我如何处理它以非阻塞方式执行 我的 apache 基准测试需要 12 秒来执行大约 30 个并发请求 我如何以更好的方式实施这一
  • Ruby 中的任务/未来

    代表潜在延迟的异步计算并且可以订阅其完成的模式的惯用 Ruby 模拟是什么 即类似于 NET 的东西System Threading Task 或Python 3 xconcurrent futures future 请注意 这并不一定意味
  • 如何组合两个 SwiftyJSON 对象

    我有一个 swiftyJSON 对象 例如 location http img http commentCount 0 timestamp 1432460217550 我希望能够向其附加另一个 swiftyJSON 对象 使其看起来像 lo
  • 保存来自 TrueDepth 相机的深度图像

    我正在尝试保存 iPhone X TrueDepth 相机的深度图像 使用AVCam照片滤镜 https developer apple com library content samplecode AVCamPhotoFilter Lis
  • Swift:检查 UISearchBar.text 是否包含 url

    如何检查 UISearchBar text 是否包含 URL 我想做这样的事情 if searchBar text NSTextCheckingType Link 但我收到错误 String is not convertible to NS
  • 带约束的嵌套集合视图的意外行为 (Swift 4)

    我的表格视图中有一个单元格 其中包含水平分页集合视图 该集合视图的每个页面内都有一个垂直集合视图 为了避免 滚动滚动 问题 我在垂直集合视图中禁用了垂直滚动 垂直集合视图的单元格计数不是静态的 可以是任意数字 因此 这会产生一个问题 集合视
  • UILabel 中的文本未垂直居中

    我使用以下代码创建了一个标签 func setupValueLabel valueLabel numberOfLines 1 valueLabel font UIFont name Avenir Black size 50 valueLab
  • XCode 7 中的 AWSS3TransferManagerUploadRequest

    我今天升级到 Xcode 7 Swift 2 0 我的项目正在使用 CocoaPods 我正在 POD 文件中导入所有与 AWS 相关的文件 我已经设置了桥接标头 并导入了 Amazon 告诉我的所有文件 在升级到 Swift 2 0 之前
  • Perl Parallel::Forkmanager 不允许收集变量值

    也许因为子进程不知道我的散列 请参阅下面的代码 散列 输出没有收集任何内容 除了写入 tmp 文件之外 还有其他方法来收集该值吗 foreach Item AllItems pid pm gt start Item and next Tem
  • 在 React Native 中将 Swift 事件发送到 Javascript 的正确流程

    我一直在尝试使用 Swift 构建全面的蓝牙功能 我目前陷入了如何将事件从 Swift 发送回 React Native 的困境 我尝试过this https stackoverflow com questions 31870775 rea
  • 如何在 Swift 中使用未知密钥解码 JSON 响应?

    我想将数据拆分为https blockchain info ticker https blockchain info ticker这样每一行都是它自己的String在一个数组中 我正在制作一个获取所选货币价格的应用程序 因此 如果有人想要澳
  • UITableView 快速获取 titleForHeadersInSection

    我想在 UITableView 的部分中设置标题的标题 语法是什么swift设置该部分中标题的标题 func tableView tableView UITableView titleForHeaderInSection section I
  • 将自定义图像设置为 UIBarButtonItem 但它不显示任何图像

    我想将自定义图像设置为 UIBarButtonItem 但它只显示周围的矩形框并且不显示实际图像 func setupBrowserToolbar let browser UIToolbar frame CGRect x 0 y 20 wi
  • 对成员“buildBlock()”的引用不明确

    我一直在尝试使用 Swift UI 为 iOS 13 制作一个应用程序 但我不断收到这个奇怪的错误 对成员 buildBlock 的引用不明确 无论我做什么 错误都不会消失 我尝试一次对代码段进行注释 以查看哪一部分可能导致了问题 但唯一有
  • 以编程方式从底部裁剪图像

    我正在开发自定义相机应用程序 一切进展顺利 但我在从底部裁剪图像时遇到了问题 即 裁剪后的图像与原始图像具有完全相同的宽度 但高度将为原始图像的 1 3 并且必须从底部开始 斯威夫特3解决方案 func cropBottomImage im
  • 在 swift 中将简单字符串转换为 JSON 字符串

    我知道有一个同标题的问题here https stackoverflow com questions 30825755 convert string to json string in swift 但在那个问题中 他试图将字典转换为 JSO
  • Chrome 扩展同步调用 - 仅在窗口关闭后创建窗口

    我有这个代码 function voteNewWindow mailNum chrome windows create url http www google com incognito true function window conso
  • 设置/覆盖 UICollectionView 中单元格之间的填充

    我有一个 UICollectionView 但在获取单元格之间的填充时遇到了问题 理论上 我应该能够将屏幕除以 4 并且我可以获得包含 4 个图像的单元格大小 完美地占据屏幕宽度 但是 它选择不这样做 相反 它会创建 3 个具有巨大填充的图

随机推荐

  • 如何在流畅的nhibernate中将复合主键映射到外国?

    我有以下表格 table A FOO PK CLIENT PK table B BAR PK CLIENT PK FK FOO FK PK gt 主键 FK gt 外键 A 和 B 之间存在一对多关系 我不能简单地这样做 class AMa
  • Flutter - 自定义按钮点击区域

    我正在构建一个 Flutter 应用程序 其中屏幕的很大一部分将被圆形按钮占据 我已经尝试了几种不同的方法来创建圆形按钮 但我总是遇到同样的问题 可点击 区域实际上不是圆形的 而是矩形的 这是一个使用以下方法获得的示例FloatingAct
  • 按任意键对元组列表进行排序

    order w x a z object a object x object z object a object w 如何根据 order 提供的键列表按第二个元素对上面的元组列表进行排序 2013 年 11 月 18 日更新 我发现了一个
  • 将值加载到 Selectize.js 中

    Problem 我有一个文本输入 我选择它作为标签 它可以很好地查询远程数据 我可以使用它搜索甚至创建新项目 并且一切正常 使用选择 var select authorsearch selectize valueField AuthorId
  • 使用 JavaScript 切换 CSS 类 - 错误

    我正在尝试制作一个脚本 当用户单击具有 burger nav img 类的图像时 它会在另一个元素中切换 open 类 我的代码是 HTML
  • Swing - 使用 getComponent() 更新所有 JButton

    我正在制作一个井字棋游戏 其中每个棋盘都由一个 JButton 代表 当有人单击该按钮时 文本将更改为 X 或 O 我正在编写一个重置函数 它将所有按钮中的文本重置为 我正在使用 getComponents 方法访问数组中的所有按钮 我只是
  • 为 GridView 的 PagerTemplate 动态生成页面链接按钮

    从 MSDN 页面获取PagerTemplate of the GridView控制 强调我的 通常 按钮控制添加到寻呼机模板中以执行寻呼操作 当单击 CommandName 属性设置为 Page 的按钮控件时 GridView 控件将执行
  • 是否可以在不使用 ApplicationContextAware 的情况下检索具有原型范围的 Spring bean

    使用Spring 3 1 如果我想检索具有原型范围的 bean 即我每次都想要该类的不同实例 是否可以在不使用 ApplicationContextaware 类的情况下检索该 bean 这就是我目前的做法 Component Qualif
  • ModuleNotFoundError:没有名为“建模”的模块

    我对深度学习和 python 非常陌生 我正在尝试重新创建该项目https github com Nagakiran1 Extending Google BERT as Question and Answering model and Ch
  • 能否阻止单个应用程序的 Microsoft 错误报告?

    我们有一个非托管 C 应用程序 它利用第 3 方 API 来读取 CAD 文件 对于某些损坏的 CAD 文件 第 3 方库崩溃并导致我们的 EXE 崩溃 因此 我们的主应用程序是一个单独的 EXE 这样它就不会受到崩溃的影响 然而 我们最终
  • 使用 java POI 插入表时 Open Office writer 崩溃

    我正在尝试使用 open office 使用 apache poi 以 docx 格式插入表 但是每次打开文件时文件都会崩溃 XWPFDocument document new XWPFDocument FileOutputStream o
  • 单击浏览器的后退按钮时重定向到特定页面

    这是一个有点菜鸟的问题 但事实就是这样 我有一个网站 用户只能通过下一页和后退按钮从一个页面导航到另一个页面 即有一个用户必须遵循的向导 向导的一部分包含不同的页面 一部分包含相同的页面但具有更改的 div 但从用户体验来看 它是不同的页面
  • Stream_Copy_To_Stream() 的替代方案 php

    我现在正在开发一个文件共享网站 遇到了一个小问题 我正在使用上传脚本 uploadify 它工作得很好 但如果用户想要 我希望上传的文件被加密 现在我有可以执行此操作的工作代码 如下所示 但我的服务器只有 1GB 或内存 并且使用strea
  • 如何在 WooCommerce 3+ 中进行调试

    我正在使用本教程为 Woocommerce 创建自定义运输方法https docs woocommerce com document shipping method api https docs woocommerce com docume
  • 如何“重置”给定 HTML 元素的样式?

    我正在开发一个可嵌入的 javascript 它将 HTML 元素插入未知页面 我无法控制要插入 HTML 的页面的样式表 问题是我插入的 HTML 会被页面错误地风格化 我想防止这种情况发生 确保我插入的元素是最不冗长和 或资源密集的ex
  • 使用 pd.read_clipboard 复制多索引数据帧?

    Given a 像这样的数据框 https stackoverflow com questions 17921010 how to query multiindex index columns values in pandas C A B
  • Swift Codable:使用未知键解码字典

    Codable当您了解 JSON 数据的关键格式时 这会非常有用 但是如果您不知道密钥怎么办 我目前面临这个问题 通常我希望 JSON 数据像这样返回 id lt 123 gt data id
  • 如何用颜色填充位图对象中的封闭区域

    给区域内的点赋予颜色来填充区域 类似于油漆桶功能中的 绘图 这 NET Framework 中 没有直接的等效项 但我希望使用 C 来做到这一点 是否可以 这是一个非常简单的洪水填充算法 应该可以帮助您入门 void Form1 Paint
  • 在不知道父元素的情况下访问多维数组元素

    我有返回以下多维数组的函数 我无法控制数组的形成方式 我正在尝试访问 结果 元素 这个问题是 父元素的名称不断变化 Result 元素的位置始终相同 因为名称为 Result 是否可以在不知道父元素名称的情况下访问该元素 Array sHe
  • UITableViewCell 异步加载图像问题 - Swift

    在我的应用程序中 我构建了自己的异步图像加载类 我传入一个对象 然后它检查缓存 NSCache 是否有图像 如果没有 它将检查文件系统是否已保存图像 如果图像尚未保存 它将在后台下载图像 NSOperations 帮助 到目前为止 效果很好