Swift 3 : URL 图片使 UITableView 滚动缓慢的问题
Swift 3 : URL Image makes UITableView scroll slow issue
我有一个扩展程序可以在 UIImageView
上打印图像 URL。但我认为问题是我的 tableView
因为这个扩展太慢了。我想我需要为它打开线程。我怎样才能在这个扩展中创建一个线程,或者你知道解决这个问题的另一种方法吗?
我的代码:
extension UIImageView{
func setImageFromURl(stringImageUrl url: String){
if let url = NSURL(string: url) {
if let data = NSData(contentsOf: url as URL) {
self.image = UIImage(data: data as Data)
}
}
}
}
我认为,这里的问题是,您需要将图像缓存在 table view
中才能平滑滚动。每次您的程序调用 cellForRowAt indexPath
时,它都会再次下载图像。这需要时间。
对于缓存图像,您可以使用 SDWebImage
、Kingfisher 等库
Kingfisher 用法示例:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "identifier", for: indexPath) as! CustomCell
cell.yourImageView.kf.setImage(with: URL)
// next time, when you will use image with this URL, it will be taken from cache.
//... other code
}
希望对您有所帮助
要在后台缓存图像并更快地滚动,请使用 SDWebImage
库
imageView.sd_setImage(with: URL(string: "http://image.jpg"), placeholderImage: UIImage(named: "placeholder.png"))
您可以使用此处建议的框架,但您也可以考虑 "rolling your own" 扩展,如 this article
中所述
"All"您需要做的是:
- 使用
URLSession
下载您的图片,这是在后台线程上完成的,因此不会卡顿和缓慢滚动。
- 完成后,在主线程上更新图像视图。
拿一个
第一次尝试可能看起来像这样:
func loadImage(fromURL urlString: String, toImageView imageView: UIImageView) {
guard let url = URL(string: urlString) else {
return
}
//Fetch image
URLSession.shared.dataTask(with: url) { (data, response, error) in
//Did we get some data back?
if let data = data {
//Yes we did, update the imageview then
let image = UIImage(data: data)
DispatchQueue.main.async {
imageView.image = image
}
}
}.resume() //remember this one or nothing will happen :)
}
然后你像这样调用方法:
loadImage(fromURL: "yourUrlToAnImageHere", toImageView: yourImageView)
改进
如果您愿意,可以添加 UIActivityIndicatorView
向用户显示 "something is loading",如下所示:
func loadImage(fromURL urlString: String, toImageView imageView: UIImageView) {
guard let url = URL(string: urlString) else {
return
}
//Add activity view
let activityView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
imageView.addSubview(activityView)
activityView.frame = imageView.bounds
activityView.translatesAutoresizingMaskIntoConstraints = false
activityView.centerXAnchor.constraint(equalTo: imageView.centerXAnchor).isActive = true
activityView.centerYAnchor.constraint(equalTo: imageView.centerYAnchor).isActive = true
activityView.startAnimating()
//Fetch image
URLSession.shared.dataTask(with: url) { (data, response, error) in
//Done, remove the activityView no matter what
DispatchQueue.main.async {
activityView.stopAnimating()
activityView.removeFromSuperview()
}
//Did we get some data back?
if let data = data {
//Yes we did, update the imageview then
let image = UIImage(data: data)
DispatchQueue.main.async {
imageView.image = image
}
}
}.resume() //remember this one or nothing will happen :)
}
扩展
文章中提到的另一项改进可能是将其移至 UIImageView
上的 extension
,如下所示:
extension UIImageView {
func loadImage(fromURL urlString: String) {
guard let url = URL(string: urlString) else {
return
}
let activityView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
self.addSubview(activityView)
activityView.frame = self.bounds
activityView.translatesAutoresizingMaskIntoConstraints = false
activityView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
activityView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
activityView.startAnimating()
URLSession.shared.dataTask(with: url) { (data, response, error) in
DispatchQueue.main.async {
activityView.stopAnimating()
activityView.removeFromSuperview()
}
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
self.image = image
}
}
}.resume()
}
}
基本上它与以前的代码相同,但对 imageView
的引用已更改为 self
。
你可以这样使用它:
yourImageView.loadImage(fromURL: "yourUrlStringHere")
当然...包括 SDWebImage 或 Kingfisher 作为依赖项会更快,并且 "just works" 大多数时候,此外它还为您提供其他好处,例如图像缓存等。但我希望这个例子表明,为图像编写自己的扩展并没有那么糟糕......而且当它不起作用时你知道应该责怪谁;)
希望对您有所帮助。
你的表视图很慢,因为你在主线程的当前线程中加载数据。您应该加载其他线程的数据,然后在主线程中设置图像(因为所有 UI 作业必须在主线程中完成)。您不需要为此使用第三方库,只需更改您的扩展名:
extension UIImageView{
func setImageFromURl(stringImageUrl url: String){
if let url = NSURL(string: url) {
DispatchQueue.global(qos: .default).async{
if let data = NSData(contentsOf: url as URL) {
DispatchQueue.main.async {
self.image = UIImage(data: data as Data)
}
}
}
}
}
}
我有一个扩展程序可以在 UIImageView
上打印图像 URL。但我认为问题是我的 tableView
因为这个扩展太慢了。我想我需要为它打开线程。我怎样才能在这个扩展中创建一个线程,或者你知道解决这个问题的另一种方法吗?
我的代码:
extension UIImageView{
func setImageFromURl(stringImageUrl url: String){
if let url = NSURL(string: url) {
if let data = NSData(contentsOf: url as URL) {
self.image = UIImage(data: data as Data)
}
}
}
}
我认为,这里的问题是,您需要将图像缓存在 table view
中才能平滑滚动。每次您的程序调用 cellForRowAt indexPath
时,它都会再次下载图像。这需要时间。
对于缓存图像,您可以使用 SDWebImage
、Kingfisher 等库
Kingfisher 用法示例:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "identifier", for: indexPath) as! CustomCell
cell.yourImageView.kf.setImage(with: URL)
// next time, when you will use image with this URL, it will be taken from cache.
//... other code
}
希望对您有所帮助
要在后台缓存图像并更快地滚动,请使用 SDWebImage
库
imageView.sd_setImage(with: URL(string: "http://image.jpg"), placeholderImage: UIImage(named: "placeholder.png"))
您可以使用此处建议的框架,但您也可以考虑 "rolling your own" 扩展,如 this article
中所述"All"您需要做的是:
- 使用
URLSession
下载您的图片,这是在后台线程上完成的,因此不会卡顿和缓慢滚动。 - 完成后,在主线程上更新图像视图。
拿一个
第一次尝试可能看起来像这样:
func loadImage(fromURL urlString: String, toImageView imageView: UIImageView) {
guard let url = URL(string: urlString) else {
return
}
//Fetch image
URLSession.shared.dataTask(with: url) { (data, response, error) in
//Did we get some data back?
if let data = data {
//Yes we did, update the imageview then
let image = UIImage(data: data)
DispatchQueue.main.async {
imageView.image = image
}
}
}.resume() //remember this one or nothing will happen :)
}
然后你像这样调用方法:
loadImage(fromURL: "yourUrlToAnImageHere", toImageView: yourImageView)
改进
如果您愿意,可以添加 UIActivityIndicatorView
向用户显示 "something is loading",如下所示:
func loadImage(fromURL urlString: String, toImageView imageView: UIImageView) {
guard let url = URL(string: urlString) else {
return
}
//Add activity view
let activityView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
imageView.addSubview(activityView)
activityView.frame = imageView.bounds
activityView.translatesAutoresizingMaskIntoConstraints = false
activityView.centerXAnchor.constraint(equalTo: imageView.centerXAnchor).isActive = true
activityView.centerYAnchor.constraint(equalTo: imageView.centerYAnchor).isActive = true
activityView.startAnimating()
//Fetch image
URLSession.shared.dataTask(with: url) { (data, response, error) in
//Done, remove the activityView no matter what
DispatchQueue.main.async {
activityView.stopAnimating()
activityView.removeFromSuperview()
}
//Did we get some data back?
if let data = data {
//Yes we did, update the imageview then
let image = UIImage(data: data)
DispatchQueue.main.async {
imageView.image = image
}
}
}.resume() //remember this one or nothing will happen :)
}
扩展
文章中提到的另一项改进可能是将其移至 UIImageView
上的 extension
,如下所示:
extension UIImageView {
func loadImage(fromURL urlString: String) {
guard let url = URL(string: urlString) else {
return
}
let activityView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
self.addSubview(activityView)
activityView.frame = self.bounds
activityView.translatesAutoresizingMaskIntoConstraints = false
activityView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
activityView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
activityView.startAnimating()
URLSession.shared.dataTask(with: url) { (data, response, error) in
DispatchQueue.main.async {
activityView.stopAnimating()
activityView.removeFromSuperview()
}
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
self.image = image
}
}
}.resume()
}
}
基本上它与以前的代码相同,但对 imageView
的引用已更改为 self
。
你可以这样使用它:
yourImageView.loadImage(fromURL: "yourUrlStringHere")
当然...包括 SDWebImage 或 Kingfisher 作为依赖项会更快,并且 "just works" 大多数时候,此外它还为您提供其他好处,例如图像缓存等。但我希望这个例子表明,为图像编写自己的扩展并没有那么糟糕......而且当它不起作用时你知道应该责怪谁;)
希望对您有所帮助。
你的表视图很慢,因为你在主线程的当前线程中加载数据。您应该加载其他线程的数据,然后在主线程中设置图像(因为所有 UI 作业必须在主线程中完成)。您不需要为此使用第三方库,只需更改您的扩展名:
extension UIImageView{
func setImageFromURl(stringImageUrl url: String){
if let url = NSURL(string: url) {
DispatchQueue.global(qos: .default).async{
if let data = NSData(contentsOf: url as URL) {
DispatchQueue.main.async {
self.image = UIImage(data: data as Data)
}
}
}
}
}
}