12

私はtableviewコードで作成した(なしstoryboard)を持っています:

class MSContentVerticalList: MSContent,UITableViewDelegate,UITableViewDataSource {
var tblView:UITableView!
var dataSource:[MSC_VCItem]=[]

init(Frame: CGRect,DataSource:[MSC_VCItem]) {
    super.init(frame: Frame)
    self.dataSource = DataSource
    tblView = UITableView(frame: Frame, style: .Plain)
    tblView.delegate = self
    tblView.dataSource = self
    self.addSubview(tblView)
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return dataSource.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = UITableViewCell(style: .Subtitle, reuseIdentifier: nil)

    let record = dataSource[indexPath.row]
    cell.textLabel!.text = record.Title
    cell.imageView!.downloadFrom(link: record.Icon, contentMode: UIViewContentMode.ScaleAspectFit)
    cell.imageView!.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
    print(cell.imageView!.frame)
    cell.detailTextLabel!.text = record.SubTitle
    return cell
}
}

他のクラスには、画像を非同期でダウンロードするための拡張メソッドがあります。

   extension UIImageView
{
    func downloadFrom(link link:String?, contentMode mode: UIViewContentMode)
    {
        contentMode = mode
        if link == nil
        {
            self.image = UIImage(named: "default")
            return
        }
        if let url = NSURL(string: link!)
        {
            print("\nstart download: \(url.lastPathComponent!)")
            NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, _, error) -> Void in
                guard let data = data where error == nil else {
                    print("\nerror on download \(error)")
                    return
                }
                dispatch_async(dispatch_get_main_queue()) { () -> Void in
                    print("\ndownload completed \(url.lastPathComponent!)")
                    self.image = UIImage(data: data)
                }
            }).resume()
        }
        else
        {
            self.image = UIImage(named: "default")
        }
    }
}

私はこの機能を他の場所で使用し、正しく機能しました。ログに基づいて、画像が問題なくダウンロードされ (セルがレンダリングされたとき)、画像のダウンロード後にセル UI が更新されていないことを理解しています。

また、 Hanekeのようなキャッシング ライブラリを使用しようとしましたが、問題があり、変更されません。

間違いを理解するのを手伝ってください

ありがとう

4

5 に答える 5

11

画像を設定した後、呼び出す必要がありますself.layoutSubviews()

setNeedsLayout編集:からに修正layoutSubviews

于 2015-11-03T12:58:08.557 に答える
10

問題は、.Subtitleレンディションが返さUITableViewCellれるとすぐにセルをレイアウトすることです(画像ビューのcellForRowAtIndexPathを設定しようとする試みをオーバーライドします)。frameしたがって、非同期で画像を取得している場合、表示する画像がないかのようにセルが再レイアウトされ (画像ビューのimageプロパティを何も初期化していないため)、imageView後で非同期に更新すると、セルは、ダウンロードした画像が見えないように配置されています。

ここにはいくつかの解決策があります。

  1. URL がない場合だけでなく、URL がある場合にもdownloadFromイメージを更新できます (したがって、最初にデフォルトのイメージに設定し、後でネットワークからダウンロードしたイメージにイメージを更新します)。 default):

    extension UIImageView {
        func downloadFrom(link link:String?, contentMode mode: UIViewContentMode) {
            contentMode = mode
            image = UIImage(named: "default")
            if link != nil, let url = NSURL(string: link!) {
                NSURLSession.sharedSession().dataTaskWithURL(url) { data, response, error in
                    guard let data = data where error == nil else {
                        print("\nerror on download \(error)")
                        return
                    }
                    if let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode != 200 {
                        print("statusCode != 200; \(httpResponse.statusCode)")
                        return
                    }
                    dispatch_async(dispatch_get_main_queue()) {
                        print("\ndownload completed \(url.lastPathComponent!)")
                        self.image = UIImage(data: data)
                    }
                    }.resume()
            } else {
                self.image = UIImage(named: "default")
            }
        }
    }
    

    これにより、画像の存在に関係なくセルがレイアウトされることが保証され、画像ビューの非同期更新が機能します (一種: 以下を参照)。

  2. の動的にレイアウトされた.Subtitleレンディションを使用するのではなくUITableViewCell、イメージ ビューの固定サイズで適切にレイアウトされた独自のセル プロトタイプを作成することもできます。そうすれば、すぐに利用できる画像がない場合でも、利用可能な画像がないかのようにセルを再フォーマットすることはありません。これにより、自動レイアウトを使用してセルの書式設定を完全に制御できます。

  3. downloadFromまた、追加の 3 番目のパラメーター (ダウンロードが完了したときに呼び出すクロージャー) を取るメソッドを定義することもできます。reloadRowsAtIndexPaths次に、その閉鎖の内側を行うことができます。ただし、これは、ダウンロードした画像をキャッシュするようにこのコードを修正することを前提としています (NSCacheたとえば)。これにより、再度ダウンロードする前に、キャッシュされた画像があるかどうかを確認できます。

とはいえ、上でほのめかしたように、この基本的なパターンにはいくつかの問題があります。

  1. 下にスクロールしてから上にスクロールすると、ネットワークから画像を再度取得することになります。もう一度取得する前に、以前にダウンロードした画像をキャッシュする必要があります。

    理想的には、サーバーの応答ヘッダーが適切に構成されているため、組み込みNSURLCacheがこれを処理しますが、それをテストする必要があります。または、独自の に画像を自分でキャッシュすることもできますNSCache

  2. たとえば、100 行目まですばやくスクロールした場合、表示されなくなった最初の 99 行の画像要求の背後で、表示されているセルがバックログされることは本当に望ましくありません。画面外にスクロールするセルのリクエストを本当にキャンセルしたい。(またはdequeueCellForRowAtIndexPath、セルを再利用する を使用すると、前の要求をキャンセルするコードを記述できます。)

  3. 上で述べたように、オブジェクトdequeueCellForRowAtIndexPathを不必要にインスタンス化する必要がないようにする必要がありUITableViewCellます。それらを再利用する必要があります。

個人的には、(a) を使用し、(b) これをAlamofireImageSDWebImageDFImageManagerKingfisherなどdequeueCellForRowAtIndexPathの確立されたカテゴリのいずれかと結合することをお勧めします。必要なキャッシングと以前のリクエストのキャンセルを行うことは重要な作業であり、これらの拡張機能の 1 つを使用すると、作業が簡素化されます。また、自分でこれを行うことに決めた場合は、これらの拡張機能のコードの一部を見て、これを適切に行う方法についてのアイデアを得ることができます。UIImageViewCellUIImageView

--

たとえば、AlamofireImageを使用すると、次のことができます。

  1. カスタム テーブル ビュー セル サブクラスを定義します。

    class CustomCell : UITableViewCell {
        @IBOutlet weak var customImageView: UIImageView!
        @IBOutlet weak var customTitleLabel: UILabel!
        @IBOutlet weak var customSubtitleLabel: UILabel!
    }
    
  2. (a) の基本クラスを指定して、セル プロトタイプをテーブル ビュー ストーリーボードに追加しますCustomCell。(b) の絵コンテ ID CustomCell; (c) 画像ビューと 2 つのラベルをセル プロトタイプに追加し、サブクラスに接続@IBOutletsします。CustomCell(d) 画像ビューと 2 つのラベルの配置/サイズを定義するために必要な制約を追加します。

    自動レイアウト制約を使用して、画像ビューの寸法を定義できます

  3. あなたcellForRowAtIndexPathの は、次のようなことができます:

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("CustomCell", forIndexPath: indexPath) as! CustomCell
    
        let record = dataSource[indexPath.row]
        cell.customTitleLabel.text = record.Title
        cell.customSubtitleLabel.text = record.SubTitle
        if let urlString = record.Icon {
            cell.customImageView.af_setImageWithURL(NSURL(string: urlString)!)
        }
    
        return cell
    }
    

これにより、基本的な非同期画像の更新だけでなく、画像のキャッシュ、キューから取り出されたセルを再利用するための可視画像の優先順位付け、より効率的なものなども楽しむことができます。また、制約のあるセル プロトタイプとカスタム テーブル ビュー セル サブクラスを使用することにより、 、すべてが正しくレイアウトされているためframe、コードを手動で調整する必要がありません。

使用する拡張機能に関係なく、プロセスはほぼ同じUIImageViewですが、拡張機能を自分で作成する雑草から抜け出すことが目標です。

于 2015-11-03T17:37:11.530 に答える
2

UITableViewCell をサブクラス化して、独自のセルを作成します。プロパティが使用可能であっても、使用しているスタイル .Subtitle には画像ビューがありません。スタイル UITableViewCellStyleDefault のみがイメージ ビューを持ちます。

于 2015-11-03T16:30:00.870 に答える
2

ここにSDWebImagesライブラリを優先するのはリンクです

画像を非同期でダウンロードして画像をキャッシュし、プロジェクトへの統合も非常に簡単です

于 2016-10-07T17:06:13.557 に答える