- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我基本上遇到了与这个问题相同的问题:
Slow scroll on UITableView images
我的 UITableView
包含一个 87x123 大小的 UIImageView
。
当加载我的 UITableViewController
时,它首先调用一个循环遍历图像数组的函数。这些图像是从照片库中存储的高分辨率图像。在每次迭代中,它都会检索图像并将每个图像的大小调整为 87x123,然后将其存储回数组中的原始图像。
当所有图像都调整大小并存储后,它会调用 self.tableView.reloadData
将数组中的数据填充到单元格中。
但是,就像提到的问题一样,如果我在调整所有图像的大小并将其存储在数组中之前快速滚动,我的 UITablView
会不稳定且滞后。
这是有问题的代码:
extension UIImage
{
func resizeImage(originalImage: UIImage, scaledTo size: CGSize) -> UIImage
{
// Avoid redundant drawing
if originalImage.size.equalTo(size)
{
return originalImage
}
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
originalImage.draw(in: CGRect(x: CGFloat(0.0), y: CGFloat(0.0), width: CGFloat(size.width), height: CGFloat(size.height)))
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}
func loadImages()
{
DispatchQueue.global(qos: .background).async {
for index in 0..<self.myArray.count
{
if let image = self.myArray[index].image
{
self.myArray[index].image = image.resizeImage(originalImage: image, scaledTo: CGSize(width: 87, height: 123) )
}
if index == self.myArray.count - 1
{
print("FINISHED RESIZING ALL IMAGES")
}
}
}
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
...
// Size is 87x123
let thumbnailImage = cell.viewWithTag(1) as! UIImageView
DispatchQueue.main.async{
thumbnailImage.image = self.myArray[indexPath.row]
}
thumbnailImage.contentMode = UIViewContentMode.scaleAspectFill
thumbnailImage.layer.borderColor = UIColor.black.cgColor
thumbnailImage.layer.borderWidth = 1.0
thumbnailImage.clipsToBounds = true
return cell
}
我知道在后台线程中执行任何非 UI 操作,这就是我所做的。然后,我在 cellForRowAt
中所做的就是使用其 indexPath.row
将图像加载到单元格中。
问题是,如前所述,如果我开始滚动 UITableView
BEFORE FINISHED RESIZING ALL IMAGES
被打印出来,即之前所有图像都已调整大小,有明显的滞后和缓慢。
但是,如果我等到所有图像都已调整大小并且在滚动 UITableView
之前调用了 FINISHED RESIZING ALL IMAGES
,则滚动很流畅,没有任何滞后。
我可以放置一个加载指示器,让用户等到所有图像都已调整大小并加载到单元格中,然后再进行用户交互,但这会很烦人,因为调整所有高分辨率图像的大小大约需要 8 秒(18 张图像可调整大小)。
有没有更好的方法来解决这个延迟问题?
更新:按照@iWheelBuy 的第二个示例,我实现了以下内容:
final class ResizeOperation: Operation {
private(set) var image: UIImage
let index: Int
let size: CGSize
init(image: UIImage, index: Int, size: CGSize) {
self.image = image
self.index = index
self.size = size
super.init()
}
override func main() {
image = image.resizeImage(originalImage: image, scaledTo: size)
}
}
class MyTableViewController: UITableViewController
{
...
lazy var resizeQueue: OperationQueue = self.getQueue()
var myArray: [Information] = []
internal struct Information
{
var title: String?
var image: UIImage?
init()
{
}
}
override func viewDidLoad()
{
....
loadImages()
...
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
...
// Size is 87x123
let thumbnailImage = cell.viewWithTag(1) as! UIImageView
DispatchQueue.main.async{
thumbnailImage.image = self.myArray[indexPath.row].image
}
thumbnailImage.contentMode = UIViewContentMode.scaleAspectFill
return cell
}
func loadImages()
{
let size = CGSize(width: 87, height: 123)
for item in myArray
{
let operations = self.myArray.enumerated().map({ ResizeOperation(image: item.image!, index: $0.offset, size: size) })
operations.forEach { [weak queue = resizeQueue, weak controller = self] (operation) in
operation.completionBlock = { [operation] in
DispatchQueue.main.async { [image = operation.image, index = operation.index] in
self.update(image: image, index: index)
}
}
queue?.addOperation(operation)
}
}
}
func update(image: UIImage, index: Int)
{
myArray[index].image = image
tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: UITableViewRowAnimation.fade)
}
}
但是,在调用 tableView.reloadRows
时,我收到了一个崩溃错误:
attempt to delete row 0 from section 0, but there are only 0 sections before the update
我对它的含义和解决方法有点困惑。
最佳答案
很难确定延迟的原因。但是有一些想法可能会帮助您提高代码的性能。
尝试在开始时使用一些小图像,看看是否是原始图像的大小影响了糟糕的性能。还尝试隐藏这些行并查看是否有任何变化:
// thumbnailImage.contentMode = UIViewContentMode.scaleAspectFill
// thumbnailImage.layer.borderColor = UIColor.black.cgColor
// thumbnailImage.layer.borderWidth = 1.0
// thumbnailImage.clipsToBounds = true
您的代码有点老派,loadImages
中的 for
循环可以通过应用 map
仅用几行代码变得更具可读性或 forEach
到您的图像数组。
此外,关于您的图像数组。您从主线程读取它并从后台线程修改它。你同时做。我建议只在背景上调整图像大小......除非你确定不会有不良后果
查看下面的代码示例 #1 您当前的代码是什么样的。
另一方面,你可以走别的路。例如,您可以在开始时设置一些占位符图像,并在某些特定单元格的图像准备就绪时更新单元格。不是所有的图像一次!如果您使用一些串行队列,您将每 0.5 秒更新一次图像,并且 UI 更新会很好。
检查代码示例 #2。它没有经过测试,只是为了展示你可以走的路。
顺便说一句,您是否尝试过将 QualityOfService 从 background
更改为 userInitiated
?它可能会减少调整大小的时间......或者不会(:
DispatchQueue.global(qos: .userInitiated).async {
// code
}
示例#1
extension UIImage {
func resize(to size: CGSize) -> UIImage {
guard self.size.equalTo(size) else { return self }
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
draw(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
static func resize(images: [UIImage], size: CGSize, completion: @escaping ([UIImage]) -> Void) {
DispatchQueue.global(qos: .background).async {
let newArray = images.map({ $0.resize(to: size) })
DispatchQueue.main.async {
completion(newArray)
}
}
}
}
final class YourController: UITableViewController {
var myArray: [UIImage] = []
override func viewDidLoad() {
super.viewDidLoad()
self.loadImages()
}
}
fileprivate extension YourController {
func loadImages() {
UIImage.resize(images: myArray, size: CGSize(width: 87, height: 123)) { [weak controller = self] (newArray) in
guard let controller = controller else { return }
controller.myArray = newArray
controller.tableView.reloadData()
}
}
}
示例#2
final class ResizeOperation: Operation {
private(set) var image: UIImage
let index: Int
let size: CGSize
init(image: UIImage, index: Int, size: CGSize) {
self.image = image
self.index = index
self.size = size
super.init()
}
override func main() {
image = image.resize(to: size)
}
}
final class YourController: UITableViewController {
var myArray: [UIImage] = []
lazy var resizeQueue: OperationQueue = self.getQueue()
override func viewDidLoad() {
super.viewDidLoad()
self.loadImages()
}
private func getQueue() -> OperationQueue {
let queue = OperationQueue()
queue.qualityOfService = .background
queue.maxConcurrentOperationCount = 1
return queue
}
}
fileprivate extension YourController {
func loadImages() {
let size = CGSize(width: 87, height: 123)
let operations = myArray.enumerated().map({ ResizeOperation(image: $0.element, index: $0.offset, size: size) })
operations.forEach { [weak queue = resizeQueue, weak controller = self] (operation) in
operation.completionBlock = { [operation] in
DispatchQueue.main.async { [image = operation.image, index = operation.index] in
controller?.update(image: image, index: index)
}
}
queue?.addOperation(operation)
}
}
func update(image: UIImage, index: Int) {
myArray[index] = image
tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: UITableViewRowAnimation.fade)
}
}
关于ios - 具有调整大小的大图像的 UITableView 单元格中的初始滚动滞后?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43701594/
这是代码片段。 请说出这种用小内存存储大数据的算法是什么。 public static void main(String[] args) { long longValue = 21474836
所以我使用 imap 从 gmail 和 outlook 接收电子邮件。 Gmail 像这样编码 =?UTF-8?B?UmU6IM69zq3OvyDOtc68zrHOuc67IG5ldyBlbWFpb
很久以前就学会了 C 代码;想用 Scheme 尝试一些新的和不同的东西。我正在尝试制作一个接受两个参数并返回两者中较大者的过程,例如 (define (larger x y) (if (> x
Azure 恢复服务保管库有两个备份配置选项 - LRS 与 GRS 这是一个有关 Azure 恢复服务保管库的问题。 当其驻留区域发生故障时,如何处理启用异地冗余的恢复服务保管库?如果未为恢复服务启
说,我有以下实体: @Entity public class A { @Id @GeneratedValue private Long id; @Embedded private
我有下一个问题。 我有下一个标准: criteria.add(Restrictions.in("entity.otherEntity", getOtherEntitiesList())); 如果我的
如果这是任何类型的重复,我会提前申请,但我找不到任何可以解决我的具体问题的内容。 这是我的程序: import java.util.Random; public class CarnivalGame{
我目前正在使用golang创建一个聚合管道,在其中使用“$ or”运算符查询文档。 结果是一堆需要分组的未分组文档,这样我就可以进入下一阶段,找到两个数据集之间的交集。 然后将其用于在单独的集合中进行
是否可以在正则表达式中创建 OR 条件。 我正在尝试查找包含此类模式的文件名列表的匹配项 第一个案例 xxxxx-hello.file 或者案例二 xxxx-hello-unasigned.file
该程序只是在用户输入行数时创建菱形的形状,因此它有 6 个 for 循环; 3 个循环创建第一个三角形,3 个循环创建另一个三角形,通过这 2 个三角形和 6 个循环,我们得到了一个菱形,这是整个程序
我有一个像这样的查询字符串 www.google.com?Department=Education & Finance&Department=Health 我有这些 li 标签,它们的查询字符串是这样
我有一个带有静态构造函数的类,我用它来读取 app.config 值。如何使用不同的配置值对类进行单元测试。我正在考虑在不同的应用程序域中运行每个测试,这样我就可以为每个测试执行静态构造函数 - 但我
我正在寻找一个可以容纳多个键的容器,如果我为其中一个键值输入保留值(例如 0),它会被视为“或”搜索。 map, int > myContainer; myContainer.insert(make_
我正在为 Web 应用程序创建数据库,并正在寻找一些建议来对可能具有多种类型的单个实体进行建模,每种类型具有不同的属性。 作为示例,假设我想为“数据源”对象创建一个关系模型。所有数据源都会有一些共享属
(1) =>CREATE TABLE T1(id BIGSERIAL PRIMARY KEY, name TEXT); CREATE TABLE (2) =>INSERT INTO T1 (name)
我不确定在使用别名时如何解决不明确的列引用。 假设有两个表,a 和 b,它们都有一个 name 列。如果我加入这两个表并为结果添加别名,我不知道如何为这两个表引用 name 列。我已经尝试了一些变体,
我的查询是: select * from table where id IN (1,5,4,3,2) 我想要的与这个顺序完全相同,不是从1...5,而是从1,5,4,3,2。我怎样才能做到这一点? 最
我正在使用 C# 代码执行动态生成的 MySQL 查询。抛出异常: CREATE TABLE dump ("@employee_OID" VARCHAR(50)); "{"You have an er
我有日期 2016-03-30T23:59:59.000000+0000。我可以知道它的格式是什么吗?因为如果我使用 yyyy-MM-dd'T'HH:mm:ss.SSS,它会抛出异常 最佳答案 Sim
我有一个示例模式,它的 SQL Fiddle 如下: http://sqlfiddle.com/#!2/6816b/2 这个 fiddle 只是根据 where 子句中的条件查询示例数据库,如下所示:
我是一名优秀的程序员,十分优秀!