我基本上遇到了与此问题相同的问题:
慢速滚动图像
我的UITableView
包含一个UIImageView
,即87x123尺寸。
加载我的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
加载图像。
如前所述,问题是,如果我开始在打印出 但是,如果我等到所有图像进行调整大小,并且在滚动 我可以放置加载指示器,并让用户等到所有图像进行调整并加载到单元格中,然后再进行用户交互,但这将是一个烦人(要调整大小的18张图像(。 有更好的方法可以修复此滞后吗? update :遵循 @iwheelbuy的第二个示例,我已经实现了以下内容: 但是,在致电 尝试从第0节删除第0行,但只有0个部分 在更新之前 我对它的含义和解决方法有些困惑。FINISHED RESIZING ALL IMAGES
之前滚动 UITableView
,即在调整所有图像之前,都有明显的滞后和缓慢的。UITableView
之前调用FINISHED RESIZING ALL IMAGES
,则滚动光滑而没有任何滞后。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
时,我会收到一个错误:
很难确定滞后的原因。但是,有些想法可能会帮助您使您编码更多的性能。
尝试在开始和查看一些小图像,如果是原始图像的大小会影响不良性能。还要尝试隐藏这些行,看看是否有任何变化:
// thumbnailImage.contentMode = UIViewContentMode.scaleAspectFill
// thumbnailImage.layer.borderColor = UIColor.black.cgColor
// thumbnailImage.layer.borderWidth = 1.0
// thumbnailImage.clipsToBounds = true
您的代码有点像Oldschool,for
中的CC_13循环通过将map
或forEach
应用于您的图像数组,只需几行代码即可变得更可读取。
另外,关于您的图像。您可以从主线程中读取它,并从背景线程进行修改。然后您同时这样做。我建议只在背景上调整大小的图像...除非您确定不会有不好的后果
检查下面的代码示例#1您当前代码的外观。
另一方面,您可以采取其他方式。例如,当准备好某些特定单元格的图像时,您可以在开始时设置某些占位符图像,并更新单元格。并非所有图像一次!如果您选择一些串行队列,则每0.5秒获得图像更新,UI更新会很好。
检查代码示例#2。它没有测试,只是为了展示您可以走的方式。
顺便说一句,您是否尝试过将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)
}
}