web-dev-qa-db-ja.com

画像のダウンロード後にセルの高さを更新する

UITableViewにいくつかのテキストと画像を表示しています。画像が最初にダウンロードされます。画像がダウンロードされる前から画像のサイズがわからないので、最初は固定サイズのUIImageViewを入れました。そして、画像がダウンロードされたら、UIImageViewのサイズを変更します。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// Download image

    dispatch_async(dispatch_get_main_queue(), ^{

    // UIImageView resizing  
    });  
});

これはすべてcellForRowAtIndexPathで発生します。
ここで直面している問題は次のとおりです。
1。セルの高さを更新するにはどうすればよいですか? 1つのセルに多くの画像が存在する可能性があることを考慮してください。そのため、1つ以上のダウンロード時に下の画像の位置を変更する必要があります。
2。 UITableViewbeginUpdatesendUpdatesを使用してみましたが、セルの一番上までスクロールするため、ユーザーエクスペリエンスが低下します。

これは、reloadDataでのUIの外観です。ダウンロードする画像は5つあります: UITableViewの後のUIエクスペリエンスreloadData

16
Nitish

短い答え

  • estimatedRowHeightに十分な呼吸スペースを与えます
  • UITableViewCellによって返されたdequeueReusableCellWithIdentifierを変更すると、キャッシュされたセルでは機能しません
  • reloadRowsAtIndexPathsを使用して単一セルのリロードをトリガーします
  • Core Dataを使用してキャッシュを管理し、NSFetchedResultsControllerボイラープレートコードですべてのUI作業を実行できるようにします。

詳細に

予期しないスクロールはありません。画像が来たら更新するだけです:

  1. 更新されるセルがatまたはbelowの範囲である場合、UITableViewnotスクロールします
  2. 更新されるセルがの場合、UITableViewnotになりますスクロール
  3. UITableViewは、セルがはっきりと見える場合にのみスクロールし、およびは使用可能なスペースよりも多くのスペースを必要とします。

UITableViewAutomaticDimensionに大変な仕事をさせましょう

セルがstaleであることをCocoa Touchに通知して、newdequeueReusableCellWithIdentifierをトリガーする必要があります。 、適切な高さのセルを返します。
テーブルビュー全体またはそのセクションの1つを再読み込みし、インデックスが安定していると仮定して、-tableView:reloadRows:at:with:を呼び出し、変更したばかりのセルのindexPathを渡し、.fadeアニメーション。

コード:

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.estimatedRowHeight = 250 // match your tallest cell
    tableView.rowHeight = UITableViewAutomaticDimension
}

URLSessionを使用します。画像が利用可能になったら、reloadRows:at:withを起動します:

func loadImage(_ url: URL, indexPath: IndexPath) {
    let downloadTask:URLSessionDownloadTask =
        URLSession.shared.downloadTask(with: url, completionHandler: {
        (location: URL?, response: URLResponse?, error: Error?) -> Void in
        if let location = location {
            if let data:Data = try? Data(contentsOf: location) {
                if let image:UIImage = UIImage(data: data) {
                    self.cachedImages[indexPath.row] = image // Save into the cache
                    DispatchQueue.main.async(execute: { () -> Void in
                        self.tableView.beginUpdates()
                        self.tableView.reloadRows(
                            at: [indexPath],
                            with: .fade)
                        self.tableView.endUpdates()
                    })
                }
            }
        }
    })
    downloadTask.resume()
}

キャッシュに入ると、cellForRowはUIスレッドからキャッシュに読み込むだけです。

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
    let cell = tableView.dequeueReusableCell(withIdentifier: "id") as! CustomCell
    cell.imageView.image = cachedImages[indexPath.row]      // Read from the cache
    return cell
}

例:*ウィキペディア*からランダムな画像のセットをフェッチします

Xcode demo

►このソリューションは GitHub で、詳細は Swift Recipes で見つけてください。

17
SwiftArchitect