【问题标题】:how to implement lazy loading of images in table view using swift如何使用swift在表格视图中实现图像的延迟加载
【发布时间】:2015-04-26 00:11:37
【问题描述】:

我想使用 swift 为我的表格视图使用延迟加载概念。在我的表格视图中,我显示了多个包含产品图像和产品名称的单元格。 请帮我解决。

【问题讨论】:

标签: ios iphone swift uitableview


【解决方案1】:

旧解决方案:

因为你没有显示任何代码。

这是给你的例子。

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    // try to reuse cell
    let cell:CustomCell = tableView.dequeueReusableCellWithIdentifier("DealCell") as CustomCell

    // get the deal image
    let currentImage = deals[indexPath.row].imageID
    let unwrappedImage = currentImage
    var image = self.imageCache[unwrappedImage]
    let imageUrl = NSURL(string: "http://staging.api.cheapeat.com.au/deals/\(unwrappedImage)/photo")

    // reset reused cell image to placeholder
    cell.dealImage.image = UIImage(named: "placeholder")

    // async image
    if image == nil {

    let request: NSURLRequest = NSURLRequest(URL: imageUrl!)

    NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!,data: NSData!,error: NSError!) -> Void in
        if error == nil {

            image = UIImage(data: data)

            self.imageCache[unwrappedImage] = image
            dispatch_async(dispatch_get_main_queue(), {
                cell.dealImage.image = image

            })
        }
        else {

        }
    })
    }

    else{
        cell.dealImage.image = image
    }

  return cell

}

关注THIS 教程了解更多信息。希望这会对你有所帮助。

新解决方案:

这是我的朋友Leo Dabus 为它创建的扩展,使用起来非常简单:

extension UIImageView {
    func downloadImageFrom(link link:String, contentMode: UIViewContentMode) {
        NSURLSession.sharedSession().dataTaskWithURL( NSURL(string:link)!, completionHandler: {
            (data, response, error) -> Void in
            dispatch_async(dispatch_get_main_queue()) {
                self.contentMode =  contentMode
                if let data = data { self.image = UIImage(data: data) }
            }
        }).resume()
    }
}

现在在您的 cellForRowAtIndexPath 方法中以这种方式将图像分配给单元格:

cell.cellImageView.image = UIImage(named: "placeholder")  //set placeholder image first.
cell.cellImageView.downloadImageFrom(link: imageLinkArray[indexPath.row], contentMode: UIViewContentMode.ScaleAspectFit)  //set your image from link array.

正如Rob 在评论中建议的那样,这里有一些有用的库可供您使用:

  1. https://github.com/Alamofire/AlamofireImage
  2. https://github.com/onevcat/Kingfisher
  3. https://github.com/rs/SDWebImage
  4. https://github.com/kean/DFImageManager

【讨论】:

  • 这是一个很好的尝试,但有几个缺陷: 1. 如果在请求完成时单元格被重用,您可能会更新错误的行。 2. 如果用户快速滚动表格视图,对可见单元格的请求可能会在对不再可见单元格的请求后面记录下来。我们可以解决这些问题,但我可能会建议使用 SDWebImage 或 AFNetworking 提供的UIImageView 类别/扩展。
  • 是的,它的严重缺陷。图像必须从 reloadTableView 渲染
  • @DharmeshKheni 这个扩展看起来很熟悉 ;)
  • 这样更好,在琐碎的情况下也很好。但是,如果在慢速网络或图像视图可能被重用的情况下(例如表格视图),就会出现问题。如果可以重用图像视图,它不会考虑取消先前的请求(这意味着可见单元格请求将积压在不再可见的行的请求之后,并且您会看到图像闪烁作为各种异步图像请求结束)。它也不做缓存(NSURLSession 自动提供的除外)。
  • 我仍然会考虑使用更丰富的UIImageView 扩展之一,例如github.com/Alamofire/AlamofireImagegithub.com/onevcat/Kingfishergithub.com/rs/SDWebImagegithub.com/kean/DFImageManager
【解决方案2】:

由于我还不能发表评论,这里是 Leo Dabus 提供的有用扩展的 Swift 3(Xcode 8 Beta 6)版本。

extension UIImageView {
    func downloadImageFrom(link:String, contentMode: UIViewContentMode) {
        URLSession.shared.dataTask( with: NSURL(string:link)! as URL, completionHandler: {
            (data, response, error) -> Void in
            DispatchQueue.main.async {
                self.contentMode =  contentMode
                if let data = data { self.image = UIImage(data: data) }
            }
        }).resume()
    }
}

我在填充表格单元格的类中使用它,它在这种情况下工作得很好,以防万一任何新手想知道它是否会:

albumArt.image = UIImage(named: "placeholder")
albumArt.downloadImageFrom(link: "http://someurl.com/image.jpg", contentMode: UIViewContentMode.scaleAspectFit)

【讨论】:

  • 工作就像魅力!谢谢。
  • 无法下载图像...出现错误:TIC SSL 信任错误、NSURLSession/NSURLConnection HTTP 加载失败 (kCFStreamErrorDomainSSL, -9813) 和任务 。 HTTP 加载失败(错误代码:-1202)。有什么解决办法吗?
  • @JayprakashDubey 您是否尝试从 HTTPS 资源加载?现在 iOS 中有一些限制需要 HTTPS 连接。
  • @BradRoot :通过连接移动数据而不是办公室 WiFi 解决了这个问题。办公室 WiFi 有安全限制。
【解决方案3】:

详情

  • Xcode 10.2.1 (10E1001)、Swift 5

完整样本

Info.plist(加值)

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

播客文件

target 'stackoverflow-28694645' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!

  # Pods for stackoverflow-28694645
  pod 'Alamofire'
  pod 'AlamofireImage'
end

代码

import UIKit
import Alamofire
import AlamofireImage

class ViewController: UIViewController {

    private weak var tableView: UITableView?
    private var items = [ItunceItem]()

    override func viewDidLoad() {
        super.viewDidLoad()
        let tableView = UITableView()
        view.addSubview(tableView)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
        tableView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
        tableView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
        tableView.tableFooterView = UIView()
        tableView.register(TableViewCell.self, forCellReuseIdentifier: "TableViewCell")
        tableView.rowHeight = 100
        tableView.separatorColor = .clear
        self.tableView = tableView
        loadData()
    }

    private func loadData() {
        let urlString = "https://itunes.apple.com/search?term=navigator"
        Alamofire.request(urlString).response { [weak self] response in
            guard let self = self, let data = response.data else { return }
            do {
                let decoder = JSONDecoder()
                decoder.keyDecodingStrategy = .convertFromSnakeCase
                self.items = try decoder.decode(ItunceItems.self, from: data).results
                DispatchQueue.main.async { [weak self] in
                    guard let tableView = self?.tableView else { return }
                    tableView.delegate = self
                    tableView.dataSource = self
                    tableView.reloadData()
                }
            } catch let error { print("\(error.localizedDescription)") }
        }
    }
}

extension ViewController: UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int { return 1 }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return items.count }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
        return cell
    }
}

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        guard   let cell = cell as? TableViewCell,
                let imageUrlString = items[indexPath.row].artworkUrl100,
                let url = URL(string: imageUrlString) else { return }
        cell.photoImageView?.af_setImage(withURL: url)
    }
    func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        guard let cell = cell as? TableViewCell else { return }
        cell.photoImageView?.af_cancelImageRequest()
    }
}

struct ItunceItems: Codable { let results: [ItunceItem] }
struct ItunceItem: Codable { var artworkUrl100: String? }

class TableViewCell: UITableViewCell {

    private(set) weak var photoImageView: UIImageView?
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        selectionStyle = .none
        let imageView = UIImageView()
        addSubview(imageView)
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 6).isActive = true
        imageView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor).isActive = true
        imageView.leftAnchor.constraint(equalTo: safeAreaLayoutGuide.leftAnchor).isActive = true
        imageView.rightAnchor.constraint(equalTo: safeAreaLayoutGuide.rightAnchor).isActive = true
        imageView.contentMode = .scaleAspectFit
        photoImageView = imageView
    }

    required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
    override func prepareForReuse() {
        super.prepareForReuse()
        photoImageView?.image = nil
    }
}

结果

【讨论】:

  • 你做了pod install吗?
  • 好的..会检查的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多