web-dev-qa-db-ja.com

swiftを使用して、テーブルビューで画像の遅延読み込みを実装する方法

Swiftを使用して、テーブルビューに遅延読み込みの概念を使用します。テーブルビューでは、製品画像と製品名を含む複数のセルを表示しています。解決策を手伝ってください。

17
Vikram Pote

旧ソリューション:

コードを表示しないので。

以下に例を示します。

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
36
Dharmesh

まだコメントできないため、Leo Dabusが提供する便利な拡張機能のSwift(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)
13
Brad Root

詳細

  • 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
    }
}

結果

enter image description here

5